Skip to content

Commit

Permalink
Merge pull request #5688 from xmake-io/hashset
Browse files Browse the repository at this point in the history
improve hashset
  • Loading branch information
waruqi authored Oct 7, 2024
2 parents 6c973f5 + 5995fe8 commit 669596b
Show file tree
Hide file tree
Showing 2 changed files with 168 additions and 53 deletions.
26 changes: 26 additions & 0 deletions tests/modules/hashset/test.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import("core.base.hashset")

function test_hashset(t)
local h = hashset.of(1, 2, 3, 5, 5, 7, 1, 9, 4, 6, 8, 0)
t:require(h:size() == 10)
t:require_not(h:empty())
for item in h:items() do
t:require(h:has(item))
t:require_not(h:has(item + 10))
end
local prev = -1
for item in h:orderitems() do
t:require(item > prev)
prev = item
end
local h2 = h:clone()
t:require(h == h2)
h2:insert(11)
t:require_not(h == h2)
h2:remove(11)
t:require(h == h2)
h2:clear()
t:require(h2:empty())
t:require(h == hashset.from(h:to_array()))
end

195 changes: 142 additions & 53 deletions xmake/core/base/hashset.lua
Original file line number Diff line number Diff line change
Expand Up @@ -14,32 +14,27 @@
--
-- Copyright (C) 2015-present, TBOOX Open Source Group.
--
-- @author OpportunityLiu
-- @author OpportunityLiu, ruki
-- @file hashset.lua
--

-- define module
local hashset = hashset or {}
local hashset_impl = hashset.__index or {}

-- load modules
local table = require("base/table")
local todisplay = require("base/todisplay")
local object = require("base/object")
local table = require("base/table")
local todisplay = require("base/todisplay")

-- representaion for nil key
hashset._NIL = setmetatable({}, { __todisplay = function() return "${reset}${color.dump.keyword}nil${reset}" end, __tostring = function() return "symbol(nil)" end })
-- define module
local hashset = hashset or object { _init = {"_DATA", "_SIZE"} }

function hashset:__todisplay()
return string.format("hashset${reset}(%s) {%s}", todisplay(self._SIZE), table.concat(table.imap(table.keys(self._DATA), function (i, k)
if i > 10 then
return nil
elseif i == 10 and self._SIZE ~= 10 then
return "..."
else
return todisplay(k)
end
end), ", "))
end
-- representaion for nil key
hashset._NIL = setmetatable({}, {
__todisplay = function()
return "${reset}${color.dump.keyword}nil${reset}"
end,
__tostring = function()
return "symbol(nil)"
end
})

function hashset._to_key(key)
if key == nil then
Expand All @@ -48,35 +43,43 @@ function hashset._to_key(key)
return key
end

-- make a new hashset
function hashset.new()
return setmetatable({ _DATA = {}, _SIZE = 0 }, hashset)
end

-- construct from list of items
function hashset.of(...)
local result = hashset.new()
local data = table.pack(...)
for i = 1, data.n do
result:insert(data[i])
-- h1 == h1?
function hashset:__eq(h)
if self._DATA == h._DATA then
return true
end
return result
if self:size() ~= h:size() then
return false
end
for item in h:items() do
if not self:has(item) then
return false
end
end
return true
end

-- construct from an array
function hashset.from(array)
assert(array)
return hashset.of(table.unpack(array))
-- to display
function hashset:__todisplay()
return string.format("hashset${reset}(%s) {%s}", todisplay(self._SIZE), table.concat(table.imap(table.keys(self._DATA), function (i, k)
if i > 10 then
return nil
elseif i == 10 and self._SIZE ~= 10 then
return "..."
else
return todisplay(k)
end
end), ", "))
end

-- check value is in hashset
function hashset_impl:has(value)
function hashset:has(value)
value = hashset._to_key(value)
return self._DATA[value] or false
end

-- insert value to hashset, returns false if value has already in the hashset
function hashset_impl:insert(value)
function hashset:insert(value)
value = hashset._to_key(value)
local result = not (self._DATA[value] or false)
if result then
Expand All @@ -87,7 +90,7 @@ function hashset_impl:insert(value)
end

-- remove value from hashset, returns false if value is not in the hashset
function hashset_impl:remove(value)
function hashset:remove(value)
value = hashset._to_key(value)
local result = self._DATA[value] or false
if result then
Expand All @@ -98,25 +101,80 @@ function hashset_impl:remove(value)
end

-- convert hashset to an array, nil in the set will be ignored
function hashset_impl:to_array()
function hashset:to_array()
local result = {}
for k, _ in pairs(self._DATA) do
if k ~= hashset._NIL then
table.insert(result, k)
for item in self:items() do
if item ~= nil then
table.insert(result, item)
end
end
return result
end

-- iterate keys of hashtable
-- iterate items
--
-- @code
-- for item in instance:items() do
-- ...
-- end
-- @endcode
--
function hashset:items()
return function (t, item)
local k, _ = next(t._DATA, item)
if k == hashset._NIL then
return nil
else
return k
end
end, self, nil
end

-- iterate order items
--
-- @code
-- for item in instance:orderitems() do
-- ...
-- end
-- @endcode
--
function hashset:orderitems()
local orderkeys = table.orderkeys(self._DATA, function (a, b)
if a == hashset._NIL then
a = math.inf
end
if b == hashset._NIL then
b = math.inf
end
if type(a) == "table" then
a = tostring(a)
end
if type(b) == "table" then
b = tostring(b)
end
return a < b
end)
local i = 1
return function (t, k)
k = orderkeys[i]
i = i + 1
if k == hashset._NIL then
return nil
else
return k
end
end, self, nil
end

-- iterate keys (deprecated, please use items())
--
-- @code
-- for _, key in instance:keys() do
-- ...
-- end
-- @endcode
--
function hashset_impl:keys()
function hashset:keys()
return function (t, key)
local k, _ = next(t._DATA, key)
if k == hashset._NIL then
Expand All @@ -127,15 +185,15 @@ function hashset_impl:keys()
end, self, nil
end

-- order keys iterator
-- iterate order keys (deprecated, please use orderitems())
--
-- @code
-- for _, key in instance:orderkeys() do
-- ...
-- end
-- @endcode
--
function hashset_impl:orderkeys()
function hashset:orderkeys()
local orderkeys = table.keys(self._DATA)
table.sort(orderkeys, function (a, b)
if a == hashset._NIL then
Expand Down Expand Up @@ -165,26 +223,57 @@ function hashset_impl:orderkeys()
end

-- get size of hashset
function hashset_impl:size()
function hashset:size()
return self._SIZE
end

-- is empty?
function hashset_impl:empty()
function hashset:empty()
return self:size() == 0
end

-- get data of hashset
function hashset_impl:data()
function hashset:data()
return self._DATA
end

-- clear hashset
function hashset_impl:clear()
function hashset:clear()
self._DATA = {}
self._SIZE = 0
end

-- return module
hashset.__index = hashset_impl
-- clone hashset
function hashset:clone()
local h = hashset.new()
h._SIZE = self._SIZE
h._DATA = table.clone(self._DATA)
return h
end

-- construct from list of items
function hashset.of(...)
local result = hashset.new()
local data = table.pack(...)
for i = 1, data.n do
result:insert(data[i])
end
return result
end

-- construct from an array
function hashset.from(array)
local result = hashset.new()
for i = 1, #array do
result:insert(array[i])
end
return result
end

-- new hashset
function hashset.new()
return hashset {{}, 0}
end

-- return module: hashset
return hashset

0 comments on commit 669596b

Please sign in to comment.