summaryrefslogtreecommitdiff
path: root/macros/luatex/generic/penlight/pl/Map.lua
blob: 572b22ad0b808b90a33d8ccfaae2ccdf8c9e20d9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
--- A Map class.
--
--    > Map = require 'pl.Map'
--    > m = Map{one=1,two=2}
--    > m:update {three=3,four=4,two=20}
--    > = m == M{one=1,two=20,three=3,four=4}
--    true
--
-- Dependencies: `pl.utils`, `pl.class`, `pl.tablex`, `pl.pretty`
-- @classmod pl.Map

local tablex = require 'pl.tablex'
local utils = require 'pl.utils'
local stdmt = utils.stdmt
local deepcompare = tablex.deepcompare

local pretty_write = require 'pl.pretty' . write
local Map = stdmt.Map
local Set = stdmt.Set

local class = require 'pl.class'

-- the Map class ---------------------
class(nil,nil,Map)

function Map:_init (t)
    local mt = getmetatable(t)
    if mt == Set or mt == Map then
        self:update(t)
    else
        return t -- otherwise assumed to be a map-like table
    end
end


local function makelist(t)
    return setmetatable(t, require('pl.List'))
end

--- list of keys.
Map.keys = tablex.keys

--- list of values.
Map.values = tablex.values

--- return an iterator over all key-value pairs.
function Map:iter ()
    return pairs(self)
end

--- return a List of all key-value pairs, sorted by the keys.
function Map:items()
    local ls = makelist(tablex.pairmap (function (k,v) return makelist {k,v} end, self))
    ls:sort(function(t1,t2) return t1[1] < t2[1] end)
    return ls
end

--- set a value in the map if it doesn't exist yet.
-- @param key the key
-- @param default value to set
-- @return the value stored in the map (existing value, or the new value)
function Map:setdefault(key, default)
    local val = self[key]
    if val ~= nil then
        return val
    end
    self:set(key,default)
   return default
end

--- size of map.
-- note: this is a relatively expensive operation!
-- @class function
-- @name Map:len
Map.len = tablex.size

--- put a value into the map.
-- This will remove the key if the value is `nil`
-- @param key the key
-- @param val the value
function Map:set (key,val)
    self[key] = val
end

--- get a value from the map.
-- @param key the key
-- @return the value, or nil if not found.
function Map:get (key)
    return rawget(self,key)
end

local index_by = tablex.index_by

--- get a list of values indexed by a list of keys.
-- @param keys a list-like table of keys
-- @return a new list
function Map:getvalues (keys)
    return makelist(index_by(self,keys))
end

--- update the map using key/value pairs from another table.
-- @tab table
-- @function Map:update
Map.update = tablex.update

--- equality between maps.
-- @within metamethods
-- @tparam Map m another map.
function Map:__eq (m)
    -- note we explicitly ask deepcompare _not_ to use __eq!
    return deepcompare(self,m,true)
end

--- string representation of a map.
-- @within metamethods
function Map:__tostring ()
    return pretty_write(self,'')
end

return Map