Skip to content

Commit f51af8d

Browse files
committed
feat(Set): add equality method and __eq metamethod
1 parent c8e0c55 commit f51af8d

3 files changed

Lines changed: 84 additions & 45 deletions

File tree

spec/Set_spec.lua

Lines changed: 37 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -23,33 +23,35 @@ describe("mods.Set", function()
2323
-- stylua: ignore
2424
local tests = {
2525
--------------fname-------------|--set--|-param-|-expected-|-same_ref?---
26-
{ "add" , _b_d_ , "c" , _bcd_ , true },
27-
{ "clear" , abcde , nil , {} , true },
28-
{ "contains" , abcde , "a" , true , },
29-
{ "contains" , abcde , "z" , false , },
30-
{ "copy" , abcde , nil , abcde , },
31-
{ "difference_update" , a_c_e , _bcd_ , a___e , true },
32-
{ "difference" , a_c_e , _bcd_ , a___e , },
33-
{ "intersection_update" , a_c_e , _bcd_ , __c__ , true },
34-
{ "intersection" , a_c_e , _bcd_ , __c__ , },
35-
{ "isdisjoint" , _b_d_ , ___de , false , },
36-
{ "isdisjoint" , _b_d_ , a___e , true , },
37-
{ "isempty" , _____ , nil , true , },
38-
{ "isempty" , abcde , nil , false , },
39-
{ "issubset" , abcd_ , abcde , true , },
40-
{ "issubset" , abcde , abcd_ , false , },
41-
{ "issuperset" , abcd_ , abcde , false , },
42-
{ "issuperset" , abcde , abcd_ , true , },
43-
{ "len" , _____ , nil , 0 , },
44-
{ "len" , a_c__ , nil , 2 , },
45-
{ "map" , abc__ , upper , ABC__ , },
46-
{ "pop" , _____ , nil , nil , },
47-
{ "remove" , _bcd_ , "c" , _b_d_ , true },
48-
{ "symmetric_difference_update" , a_c_e , _bcd_ , ab_de , true },
49-
{ "symmetric_difference" , a_c_e , _bcd_ , ab_de , },
50-
{ "union" , abc__ , ___de , abcde , },
51-
{ "update" , abc__ , ___de , abcde , true },
52-
{ "values" , _____ , nil , {} , },
26+
{ "add" , _b_d_ , "c" , _bcd_ , true },
27+
{ "clear" , abcde , nil , {} , true },
28+
{ "contains" , abcde , "a" , true , },
29+
{ "contains" , abcde , "z" , false , },
30+
{ "copy" , abcde , nil , abcde , false },
31+
{ "difference_update" , a_c_e , _bcd_ , a___e , true },
32+
{ "difference" , a_c_e , _bcd_ , a___e , false },
33+
{ "equals" , abcde , abcde , true , },
34+
{ "equals" , abcde , abcd_ , false , },
35+
{ "intersection_update" , a_c_e , _bcd_ , __c__ , true },
36+
{ "intersection" , a_c_e , _bcd_ , __c__ , false },
37+
{ "isdisjoint" , _b_d_ , ___de , false , },
38+
{ "isdisjoint" , _b_d_ , a___e , true , },
39+
{ "isempty" , _____ , nil , true , },
40+
{ "isempty" , abcde , nil , false , },
41+
{ "issubset" , abcd_ , abcde , true , },
42+
{ "issubset" , abcde , abcd_ , false , },
43+
{ "issuperset" , abcd_ , abcde , false , },
44+
{ "issuperset" , abcde , abcd_ , true , },
45+
{ "len" , _____ , nil , 0 , },
46+
{ "len" , a_c__ , nil , 2 , },
47+
{ "map" , abc__ , upper , ABC__ , false },
48+
{ "pop" , _____ , nil , nil , },
49+
{ "remove" , _bcd_ , "c" , _b_d_ , true },
50+
{ "symmetric_difference_update" , a_c_e , _bcd_ , ab_de , true },
51+
{ "symmetric_difference" , a_c_e , _bcd_ , ab_de , false },
52+
{ "union" , abc__ , ___de , abcde , false },
53+
{ "update" , abc__ , ___de , abcde , true },
54+
{ "values" , _____ , nil , {} , },
5355
}
5456

5557
for i = 1, #tests do
@@ -61,9 +63,9 @@ describe("mods.Set", function()
6163
assert.are_same(expected, res)
6264

6365
if same_ref then
64-
assert.are_equal(set, res, "Expected same set instance")
66+
assert.are_equal(true, rawequal(set, res), "Expected same set instance")
6567
else
66-
assert.not_equal(set, res, "Expected different set instances")
68+
assert.are_equal(false, rawequal(set, res), "Expected different set instances")
6769
end
6870
end)
6971
end
@@ -114,6 +116,12 @@ describe("mods.Set", function()
114116
assert.are_same({ b = true, x = true }, b)
115117
end)
116118

119+
it("__eq returns set member equality", function()
120+
assert.is_true(Set({ "a", "b" }) == Set({ "b", "a" }))
121+
assert.is_false(Set({ "a", "b" }) == Set({ "a", "c" }))
122+
assert.is_false(Set({ "a", "b" }) == Set({ "a", "b", "c" }))
123+
end)
124+
117125
it("__le returns subset check", function()
118126
local a = Set({ "a" })
119127
local b = Set({ "a", "b" })

src/mods/Set.lua

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,8 @@
1-
local mods = require("mods")
1+
local tbl = require("mods.tbl")
22

33
local next = next
44
local pairs = pairs
55

6-
local tbl_map = {
7-
update = "update",
8-
values = "keys",
9-
len = "count",
10-
isempty = "isempty",
11-
}
12-
136
---@type mods.Set
147
local Set = {}
158
Set.__index = Set
@@ -49,6 +42,8 @@ function Set:difference(set)
4942
return self:copy():difference_update(set)
5043
end
5144

45+
Set.equals = tbl.same
46+
5247
function Set:intersection_update(set)
5348
for k in pairs(self) do
5449
if not set[k] then
@@ -71,6 +66,8 @@ function Set:isdisjoint(set)
7166
return true
7267
end
7368

69+
Set.isempty = tbl.isempty
70+
7471
function Set:issubset(set)
7572
for k in pairs(self) do
7673
if not set[k] then
@@ -89,6 +86,8 @@ function Set:issuperset(set)
8986
return true
9087
end
9188

89+
Set.len = tbl.count
90+
9291
function Set:contains(v)
9392
return self[v] ~= nil
9493
end
@@ -130,22 +129,18 @@ function Set:union(set)
130129
return self:copy():update(set)
131130
end
132131

132+
Set.update = tbl.update
133+
Set.values = tbl.keys
134+
133135
Set.__add = Set.union
136+
Set.__eq = tbl.same
134137
Set.__le = Set.issubset
135138
Set.__lt = function(a, b)
136139
return a:issubset(b) and not a:issuperset(b)
137140
end
138141
Set.__sub = Set.difference
139142

140143
return setmetatable(Set, {
141-
__index = function(t, k)
142-
local fname = tbl_map[k]
143-
if fname then
144-
local fn = mods.tbl[fname]
145-
rawset(t, k, fn)
146-
return fn
147-
end
148-
end,
149144
__call = function(_, t)
150145
local set = new()
151146
if t == nil then

types/Set.lua

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,26 @@ function Set:union(set) end
233233
---@nodiscard
234234
function Set:isdisjoint(set) end
235235

236+
---
237+
---Return true when both sets contain exactly the same members.
238+
---
239+
---```lua
240+
---a = Set({ "a", "b" })
241+
---b = Set({ "b", "a" })
242+
---ok = a:equals(b) --> true
243+
---```
244+
---
245+
---> [!NOTE]
246+
--->
247+
---> `equals` is also available as the `__eq` (`==`) operator.
248+
---> `a:equals(b)` is equivalent to `a == b`.
249+
---
250+
---@param self mods.Set|table<any,true>
251+
---@param set mods.Set|table<any,true>
252+
---@return boolean
253+
---@nodiscard
254+
function Set:equals(set) end
255+
236256
---
237257
---Return true if the set has no elements.
238258
---
@@ -363,6 +383,22 @@ function Set:values() end
363383
---@return mods.Set
364384
function Set.__add(self, set) end
365385

386+
---
387+
---Return true if both sets contain exactly the same members using `==`.
388+
---
389+
---```lua
390+
---ok = Set({ "a", "b" }) == Set({ "b", "a" }) --> true
391+
---```
392+
---
393+
---> [!NOTE]
394+
--->
395+
---> `__eq` is the operator form of `:equals(set)`.
396+
---
397+
---@param self mods.Set|table<any,true>
398+
---@param set mods.Set|table<any,true>
399+
---@return boolean
400+
function Set.__eq(self, set) end
401+
366402
---
367403
---Return true if the left set is a subset of the right set using `<=`.
368404
---

0 commit comments

Comments
 (0)