-
Notifications
You must be signed in to change notification settings - Fork 9
/
index.js
135 lines (120 loc) · 4.3 KB
/
index.js
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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
// ::- Persistent data structure representing an ordered mapping from
// strings to values, with some convenient update methods.
export default function OrderedMap(content) {
this.content = content
}
OrderedMap.prototype = {
constructor: OrderedMap,
find: function(key) {
for (var i = 0; i < this.content.length; i += 2)
if (this.content[i] === key) return i
return -1
},
// :: (string) → ?any
// Retrieve the value stored under `key`, or return undefined when
// no such key exists.
get: function(key) {
var found = this.find(key)
return found == -1 ? undefined : this.content[found + 1]
},
// :: (string, any, ?string) → OrderedMap
// Create a new map by replacing the value of `key` with a new
// value, or adding a binding to the end of the map. If `newKey` is
// given, the key of the binding will be replaced with that key.
update: function(key, value, newKey) {
var self = newKey && newKey != key ? this.remove(newKey) : this
var found = self.find(key), content = self.content.slice()
if (found == -1) {
content.push(newKey || key, value)
} else {
content[found + 1] = value
if (newKey) content[found] = newKey
}
return new OrderedMap(content)
},
// :: (string) → OrderedMap
// Return a map with the given key removed, if it existed.
remove: function(key) {
var found = this.find(key)
if (found == -1) return this
var content = this.content.slice()
content.splice(found, 2)
return new OrderedMap(content)
},
// :: (string, any) → OrderedMap
// Add a new key to the start of the map.
addToStart: function(key, value) {
return new OrderedMap([key, value].concat(this.remove(key).content))
},
// :: (string, any) → OrderedMap
// Add a new key to the end of the map.
addToEnd: function(key, value) {
var content = this.remove(key).content.slice()
content.push(key, value)
return new OrderedMap(content)
},
// :: (string, string, any) → OrderedMap
// Add the given key/value before `place`. If `place` is not found,
// the new key is added to the end.
addBefore: function(place, key, value) {
var without = this.remove(key), content = without.content.slice()
var found = without.find(place)
content.splice(found == -1 ? content.length : found, 0, key, value)
return new OrderedMap(content)
},
// :: ((key: string, value: any))
// Call the given function for each key/value pair in the map, in
// order.
forEach: function(f) {
for (var i = 0; i < this.content.length; i += 2)
f(this.content[i], this.content[i + 1])
},
// :: (union<Object, OrderedMap>) → OrderedMap
// Create a new map by prepending the keys in this map that don't
// appear in `map` before the keys in `map`.
prepend: function(map) {
map = OrderedMap.from(map)
if (!map.size) return this
return new OrderedMap(map.content.concat(this.subtract(map).content))
},
// :: (union<Object, OrderedMap>) → OrderedMap
// Create a new map by appending the keys in this map that don't
// appear in `map` after the keys in `map`.
append: function(map) {
map = OrderedMap.from(map)
if (!map.size) return this
return new OrderedMap(this.subtract(map).content.concat(map.content))
},
// :: (union<Object, OrderedMap>) → OrderedMap
// Create a map containing all the keys in this map that don't
// appear in `map`.
subtract: function(map) {
var result = this
map = OrderedMap.from(map)
for (var i = 0; i < map.content.length; i += 2)
result = result.remove(map.content[i])
return result
},
// :: () → Object
// Turn ordered map into a plain object.
toObject: function() {
var result = {}
this.forEach(function(key, value) { result[key] = value })
return result
},
// :: number
// The amount of keys in this map.
get size() {
return this.content.length >> 1
}
}
// :: (?union<Object, OrderedMap>) → OrderedMap
// Return a map with the given content. If null, create an empty
// map. If given an ordered map, return that map itself. If given an
// object, create a map from the object's properties.
OrderedMap.from = function(value) {
if (value instanceof OrderedMap) return value
var content = []
if (value) for (var prop in value) content.push(prop, value[prop])
return new OrderedMap(content)
}