@quenty/observablecollection 12.6.0 → 12.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,32 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [12.8.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/observablecollection@12.7.0...@quenty/observablecollection@12.8.0) (2024-10-04)
7
+
8
+
9
+ ### Features
10
+
11
+ * Add ObservableSortedList:Contains(value) ([049c9df](https://github.com/Quenty/NevermoreEngine/commit/049c9df1df0cc765124c624a1276a456ff3e7700))
12
+ * ObservableSortedList can take non rx-value and still be performant ([e5c1d4f](https://github.com/Quenty/NevermoreEngine/commit/e5c1d4fb272f22d2559fba6f1a533686a730fca8))
13
+
14
+
15
+ ### Performance Improvements
16
+
17
+ * Predeclare size of lists to allocate for lists ([b662bbb](https://github.com/Quenty/NevermoreEngine/commit/b662bbb814f91a1853a549b217fa8af8a9f74d3d))
18
+ * Use switchToBrio() and table.create ([f73644e](https://github.com/Quenty/NevermoreEngine/commit/f73644e56a9ffd0991833bd147c705c210553539))
19
+
20
+
21
+
22
+
23
+
24
+ # [12.7.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/observablecollection@12.6.0...@quenty/observablecollection@12.7.0) (2024-09-25)
25
+
26
+ **Note:** Version bump only for package @quenty/observablecollection
27
+
28
+
29
+
30
+
31
+
6
32
  # [12.6.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/observablecollection@12.5.2...@quenty/observablecollection@12.6.0) (2024-09-25)
7
33
 
8
34
  **Note:** Version bump only for package @quenty/observablecollection
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quenty/observablecollection",
3
- "version": "12.6.0",
3
+ "version": "12.8.0",
4
4
  "description": "A set of observable collections, such as sets, maps, sorted lists, and more.",
5
5
  "keywords": [
6
6
  "Roblox",
@@ -27,23 +27,23 @@
27
27
  "Quenty"
28
28
  ],
29
29
  "dependencies": {
30
- "@quenty/baseobject": "^10.5.0",
31
- "@quenty/brio": "^14.6.0",
32
- "@quenty/ducktype": "^5.5.0",
33
- "@quenty/loader": "^10.5.0",
34
- "@quenty/maid": "^3.3.0",
35
- "@quenty/promise": "^10.5.0",
36
- "@quenty/rx": "^13.6.0",
37
- "@quenty/signal": "^7.5.0",
30
+ "@quenty/baseobject": "^10.6.0",
31
+ "@quenty/brio": "^14.8.0",
32
+ "@quenty/ducktype": "^5.6.0",
33
+ "@quenty/loader": "^10.6.0",
34
+ "@quenty/maid": "^3.4.0",
35
+ "@quenty/promise": "^10.6.0",
36
+ "@quenty/rx": "^13.8.0",
37
+ "@quenty/signal": "^7.7.0",
38
38
  "@quenty/steputils": "^3.5.0",
39
- "@quenty/symbol": "^3.1.0",
40
- "@quenty/valueobject": "^13.6.0"
39
+ "@quenty/symbol": "^3.2.0",
40
+ "@quenty/valueobject": "^13.8.0"
41
41
  },
42
42
  "devDependencies": {
43
- "@quenty/blend": "^12.6.0"
43
+ "@quenty/blend": "^12.8.0"
44
44
  },
45
45
  "publishConfig": {
46
46
  "access": "public"
47
47
  },
48
- "gitHead": "41715b15e2b48b2d22ff4f5277a8d4b7a0d32ef3"
48
+ "gitHead": "035abfa088c854a73e1c65b350267eaa17669646"
49
49
  }
@@ -205,7 +205,7 @@ end
205
205
  @return Observable<number>
206
206
  ]=]
207
207
  function ObservableList:ObserveIndexByKey(key)
208
- assert(type(key) == "userdata", "Bad key")
208
+ assert(Symbol.isSymbol(key), "Bad key")
209
209
 
210
210
  return self._keyIndexObservables:Observe(key, function(sub)
211
211
  sub:Fire(self:GetIndexByKey(key))
@@ -417,9 +417,9 @@ end
417
417
  @return { T }
418
418
  ]=]
419
419
  function ObservableList:GetList()
420
- local list = {}
421
- for _, key in pairs(self._keyList) do
422
- table.insert(list, self._contents[key])
420
+ local list = table.create(#self._keyList)
421
+ for index, key in pairs(self._keyList) do
422
+ list[index] = self._contents[key]
423
423
  end
424
424
  return list
425
425
  end
@@ -184,10 +184,9 @@ function ObservableMap:ObserveAtKeyBrio(key)
184
184
  assert(key ~= nil, "Bad key")
185
185
 
186
186
  return self:ObserveAtKey(key):Pipe({
187
- RxBrioUtils.toBrio();
188
- RxBrioUtils.where(function(value)
187
+ RxBrioUtils.switchToBrio(function(value)
189
188
  return value ~= nil
190
- end)
189
+ end);
191
190
  })
192
191
  end
193
192
 
@@ -275,7 +274,7 @@ end
275
274
  @return { TValue }
276
275
  ]=]
277
276
  function ObservableMap:GetValueList()
278
- local list = {}
277
+ local list = table.create(self._countValue.Value)
279
278
  for _, value in pairs(self._map) do
280
279
  table.insert(list, value)
281
280
  end
@@ -287,7 +286,7 @@ end
287
286
  @return { TKey }
288
287
  ]=]
289
288
  function ObservableMap:GetKeyList()
290
- local list = {}
289
+ local list = table.create(self._countValue.Value)
291
290
  for key, _ in pairs(self._map) do
292
291
  table.insert(list, key)
293
292
  end
@@ -303,7 +302,10 @@ function ObservableMap:ObserveKeyList()
303
302
  local topMaid = Maid.new()
304
303
 
305
304
  -- TODO: maybe don't allocate as much here?
306
- local keyList = {}
305
+ local keyList = table.create(self._countValue.Value)
306
+ for key, _ in pairs(self._map) do
307
+ table.insert(keyList, key)
308
+ end
307
309
 
308
310
  topMaid:GiveTask(self.KeyAdded:Connect(function(addedKey)
309
311
  table.insert(keyList, addedKey)
@@ -318,10 +320,6 @@ function ObservableMap:ObserveKeyList()
318
320
  sub:Fire(table.clone(keyList))
319
321
  end))
320
322
 
321
- for key, _ in pairs(self._map) do
322
- table.insert(keyList, key)
323
- end
324
-
325
323
  sub:Fire(table.clone(keyList))
326
324
 
327
325
  return topMaid
@@ -27,6 +27,29 @@ function ObservableMapList.new()
27
27
  self._maid = Maid.new()
28
28
  self._observableMapOfLists = self._maid:Add(ObservableMap.new())
29
29
 
30
+ --[=[
31
+ Fires when an item is added
32
+ @readonly
33
+ @prop ListAdded Signal<TKey>
34
+ @within ObservableMapSet
35
+ ]=]
36
+ self.ListAdded = assert(self._observableMapOfLists.KeyAdded, "Bad KeyAdded") -- :Fire(key, set)
37
+
38
+ --[=[
39
+ Fires when an item is removed
40
+ @readonly
41
+ @prop ListRemoved Signal<TKey>
42
+ @within ObservableMapSet
43
+ ]=]
44
+ self.ListRemoved = assert(self._observableMapOfLists.KeyRemoved, "Bad KeyRemoved") -- :Fire(key)
45
+
46
+ --[=[
47
+ Fires when the count changes.
48
+ @prop CountChanged RBXScriptSignal
49
+ @within ObservableMap
50
+ ]=]
51
+ self.CountChanged = assert(self._observableMapOfLists.CountChanged, "Bad CountChanged")
52
+
30
53
  return self
31
54
  end
32
55
 
@@ -46,19 +69,19 @@ function ObservableMapList:Push(observeKey, entry)
46
69
  assert(observeKey ~= nil, "Bad observeKey")
47
70
  assert(entry ~= nil, "Bad entry")
48
71
 
49
- if not Observable.isObservable(observeKey) then
50
- observeKey = Rx.of(observeKey)
51
- end
52
-
53
72
  local maid = Maid.new()
54
73
 
55
- maid:GiveTask(observeKey:Subscribe(function(key)
56
- maid._currentAddValue = nil
74
+ if Observable.isObservable(observeKey) then
75
+ maid:GiveTask(observeKey:Subscribe(function(key)
76
+ maid._currentAddValue = nil
57
77
 
58
- if key ~= nil then
59
- maid._currentAddValue = self:_addToList(key, entry)
60
- end
61
- end))
78
+ if key ~= nil then
79
+ maid._currentAddValue = self:_addToList(key, entry)
80
+ end
81
+ end))
82
+ else
83
+ maid:GiveTask(self:_addToList(observeKey, entry))
84
+ end
62
85
 
63
86
  -- Ensure self-cleanup when map cleans up
64
87
  self._maid[maid] = maid
@@ -221,18 +244,36 @@ function ObservableMapList:GetListForKey(key)
221
244
  return self._observableMapOfLists:Get(key)
222
245
  end
223
246
 
247
+ --[=[
248
+ Observes the observable list for the given key
249
+
250
+ @param key TKey
251
+ @return Observable<ObservableList<TValue>>
252
+ ]=]
224
253
  function ObservableMapList:ObserveList(key)
225
254
  assert(key ~= nil, "Bad key")
226
255
 
227
256
  return self._observableMapOfLists:ObserveAtKey(key)
228
257
  end
229
258
 
259
+ --[=[
260
+ Observes the observable list for the given key
261
+
262
+ @param key TKey
263
+ @return Observable<Brio<ObservableList<TValue>>>
264
+ ]=]
230
265
  function ObservableMapList:ObserveListBrio(key)
231
266
  assert(key ~= nil, "Bad key")
232
267
 
233
268
  return self._observableMapOfLists:ObserveAtKeyBrio(key)
234
269
  end
235
270
 
271
+ --[=[
272
+ Observes the number of entries for the given key
273
+
274
+ @param key TKey
275
+ @return Observable<number>
276
+ ]=]
236
277
  function ObservableMapList:ObserveCountForKey(key)
237
278
  assert(key ~= nil, "Bad key")
238
279
 
@@ -249,10 +290,6 @@ function ObservableMapList:_addToList(key, entry)
249
290
  return list:Add(entry)
250
291
  end
251
292
 
252
- function ObservableMapList:_removeList(list)
253
- self._maid[list] = nil
254
- end
255
-
256
293
  function ObservableMapList:_getOrCreateList(key)
257
294
  local existing = self._observableMapOfLists:Get(key)
258
295
  if existing then
@@ -9,12 +9,10 @@ local require = require(script.Parent.loader).load(script)
9
9
 
10
10
  local Maid = require("Maid")
11
11
  local Observable = require("Observable")
12
+ local ObservableMap = require("ObservableMap")
12
13
  local ObservableSet = require("ObservableSet")
13
- local Signal = require("Signal")
14
- local Brio = require("Brio")
15
- local RxBrioUtils = require("RxBrioUtils")
16
- local ValueObject = require("ValueObject")
17
14
  local Rx = require("Rx")
15
+ local RxBrioUtils = require("RxBrioUtils")
18
16
 
19
17
  local ObservableMapSet = {}
20
18
  ObservableMapSet.ClassName = "ObservableMapSet"
@@ -28,7 +26,7 @@ function ObservableMapSet.new()
28
26
  local self = setmetatable({}, ObservableMapSet)
29
27
 
30
28
  self._maid = Maid.new()
31
- self._observableSetMap = {} -- [key] = ObservableSet<TEntry>
29
+ self._observableMapOfSets = self._maid:Add(ObservableMap.new())
32
30
 
33
31
  --[=[
34
32
  Fires when an item is added
@@ -36,7 +34,7 @@ function ObservableMapSet.new()
36
34
  @prop SetAdded Signal<TKey>
37
35
  @within ObservableMapSet
38
36
  ]=]
39
- self.SetAdded = self._maid:Add(Signal.new()) -- :Fire(key, set)
37
+ self.SetAdded = assert(self._observableMapOfSets.KeyAdded, "Bad KeyAdded") -- :Fire(key, set)
40
38
 
41
39
  --[=[
42
40
  Fires when an item is removed
@@ -44,9 +42,14 @@ function ObservableMapSet.new()
44
42
  @prop SetRemoved Signal<TKey>
45
43
  @within ObservableMapSet
46
44
  ]=]
47
- self.SetRemoved = self._maid:Add(Signal.new()) -- :Fire(key)
45
+ self.SetRemoved = assert(self._observableMapOfSets.KeyRemoved, "Bad KeyRemoved") -- :Fire(key)
48
46
 
49
- self._setCount = self._maid:Add(ValueObject.new(0, "number"))
47
+ --[=[
48
+ Fires when the count changes.
49
+ @prop CountChanged RBXScriptSignal
50
+ @within ObservableMap
51
+ ]=]
52
+ self.CountChanged = assert(self._observableMapOfSets.CountChanged, "Bad CountChanged")
50
53
 
51
54
  return self
52
55
  end
@@ -68,31 +71,19 @@ function ObservableMapSet:Push(observeKey, entry)
68
71
  assert(observeKey ~= nil, "Bad observeKey")
69
72
  assert(entry ~= nil, "Bad entry")
70
73
 
71
- if not Observable.isObservable(observeKey) then
72
- observeKey = Rx.of(observeKey)
73
- end
74
-
75
74
  local maid = Maid.new()
76
75
 
77
- local lastKey = nil
78
- local function removeLastEntry()
79
- if lastKey ~= nil then
80
- self:_removeFromObservableSet(lastKey, entry)
81
- end
82
- lastKey = nil
83
- end
84
-
85
- maid:GiveTask(observeKey:Subscribe(function(key)
86
- removeLastEntry()
76
+ if Observable.isObservable(observeKey) then
77
+ maid:GiveTask(observeKey:Subscribe(function(key)
78
+ maid._currentAddValue = nil
87
79
 
88
- if key ~= nil then
89
- self:_addToObservableSet(key, entry)
90
- end
91
-
92
- lastKey = key
93
- end))
94
-
95
- maid:GiveTask(removeLastEntry)
80
+ if key ~= nil then
81
+ maid._currentAddValue = self:_addToSet(key, entry)
82
+ end
83
+ end))
84
+ else
85
+ maid:GiveTask(self:_addToSet(observeKey, entry))
86
+ end
96
87
 
97
88
  -- Ensure self-cleanup when map cleans up
98
89
  self._maid[maid] = maid
@@ -131,12 +122,8 @@ end
131
122
  Gets a list of all keys.
132
123
  @return { TKey }
133
124
  ]=]
134
- function ObservableMapSet:GetKeyList()
135
- local list = {}
136
- for key, _ in pairs(self._observableSetMap) do
137
- table.insert(list, key)
138
- end
139
- return list
125
+ function ObservableMapSet:ObserveKeyList()
126
+ return self._observableMapOfSets:GetKeyList()
140
127
  end
141
128
 
142
129
  --[=[
@@ -144,33 +131,15 @@ end
144
131
  @return Observable<{ TKey }>
145
132
  ]=]
146
133
  function ObservableMapSet:ObserveKeyList()
147
- return Observable.new(function(sub)
148
- local topMaid = Maid.new()
149
-
150
- -- TODO: maybe don't allocate as much here?
151
- local keyList = {}
152
-
153
- topMaid:GiveTask(self.SetAdded:Connect(function(addedKey)
154
- table.insert(keyList, addedKey)
155
- sub:Fire(table.clone(keyList))
156
- end))
157
-
158
- topMaid:GiveTask(self.SetRemoved:Connect(function(removedKey)
159
- local index = table.find(keyList, removedKey)
160
- if index then
161
- table.remove(keyList, index)
162
- end
163
- sub:Fire(table.clone(keyList))
164
- end))
165
-
166
- for key, _ in pairs(self._observableSetMap) do
167
- table.insert(keyList, key)
168
- end
169
-
170
- sub:Fire(table.clone(keyList))
134
+ return self._observableMapOfSets:ObserveKeyList()
135
+ end
171
136
 
172
- return topMaid
173
- end)
137
+ --[=[
138
+ Observes all keys in the map
139
+ @return Observable<Brio<TKey>>
140
+ ]=]
141
+ function ObservableMapSet:ObserveKeysBrio()
142
+ return self._observableMapOfSets:ObserveKeysBrio()
174
143
  end
175
144
 
176
145
  --[=[
@@ -178,7 +147,7 @@ end
178
147
  @return number
179
148
  ]=]
180
149
  function ObservableMapSet:GetSetCount()
181
- return self._setCount.Value
150
+ return self._observableMapOfSets:GetCount()
182
151
  end
183
152
 
184
153
  --[=[
@@ -186,7 +155,7 @@ end
186
155
  @return Observable<number>
187
156
  ]=]
188
157
  function ObservableMapSet:ObserveSetCount()
189
- return self._setCount:Observe()
158
+ return self._observableMapOfSets:ObserveCount()
190
159
  end
191
160
 
192
161
  --[=[
@@ -197,49 +166,26 @@ end
197
166
  function ObservableMapSet:ObserveItemsForKeyBrio(key)
198
167
  assert(key ~= nil, "Bad key")
199
168
 
200
- return Observable.new(function(sub)
201
- local topMaid = Maid.new()
202
-
203
- local function connect()
204
- local maid = Maid.new()
205
-
206
- local set = self._observableSetMap[key]
169
+ return self._observableMapOfSets:ObserveAtKeyBrio(key):Pipe({
170
+ RxBrioUtils.switchMapBrio(function(set)
207
171
  if set then
208
- maid:GiveTask(set:ObserveItemsBrio():Subscribe(function(brio)
209
- sub:Fire(brio)
210
- end))
172
+ return set:ObserveItemsBrio()
173
+ else
174
+ return Rx.EMPTY
211
175
  end
212
-
213
- topMaid._current = maid
214
- end
215
-
216
- topMaid:GiveTask(self.SetAdded:Connect(function(addedKey)
217
- if addedKey == key then
218
- connect()
219
- end
220
- end))
221
-
222
- topMaid:GiveTask(self.SetRemoved:Connect(function(removedKey)
223
- if removedKey == key then
224
- connect()
225
- end
226
- end))
227
-
228
- connect()
229
-
230
- return topMaid
231
- end)
176
+ end);
177
+ })
232
178
  end
233
179
 
234
180
  --[=[
235
181
  Gets the first item for the given key
236
182
  @param key TKey
237
- @return TValue
183
+ @return TValue | nil
238
184
  ]=]
239
185
  function ObservableMapSet:GetFirstItemForKey(key)
240
186
  assert(key ~= nil, "Bad key")
241
187
 
242
- local observableSet = self._observableSetMap[key]
188
+ local observableSet = self:GetObservableSetForKey(key)
243
189
  if not observableSet then
244
190
  return nil
245
191
  end
@@ -255,7 +201,7 @@ end
255
201
  function ObservableMapSet:GetListForKey(key)
256
202
  assert(key ~= nil, "Bad key")
257
203
 
258
- local observableSet = self._observableSetMap[key]
204
+ local observableSet = self:GetObservableSetForKey(key)
259
205
  if not observableSet then
260
206
  return {}
261
207
  end
@@ -271,45 +217,27 @@ end
271
217
  function ObservableMapSet:GetObservableSetForKey(key)
272
218
  assert(key ~= nil, "Bad key")
273
219
 
274
- return self._observableSetMap[key]
220
+ return self._observableMapOfSets:Get(key)
275
221
  end
276
222
 
223
+ --[=[
224
+ Observes the observable set for the given key
225
+
226
+ @param key TKey
227
+ @return Observable<Brio<ObservableSet<TValue>>>
228
+ ]=]
277
229
  function ObservableMapSet:ObserveSetBrio(key)
278
230
  assert(key ~= nil, "Bad key")
279
231
 
280
- return Observable.new(function(sub)
281
- local topMaid = Maid.new()
282
-
283
- local function connect()
284
- local brio
285
-
286
- local set = self._observableSetMap[key]
287
- if set then
288
- brio = Brio.new(set)
289
- sub:Fire(brio)
290
- end
291
-
292
- topMaid._current = brio
293
- end
294
-
295
- topMaid:GiveTask(self.SetAdded:Connect(function(addedKey)
296
- if addedKey == key then
297
- connect()
298
- end
299
- end))
300
-
301
- topMaid:GiveTask(self.SetRemoved:Connect(function(removedKey)
302
- if removedKey == key then
303
- connect()
304
- end
305
- end))
306
-
307
- connect()
308
-
309
- return topMaid
310
- end)
232
+ return self._observableMapOfSets:ObserveAtKeyBrio(key)
311
233
  end
312
234
 
235
+ --[=[
236
+ Observes the number of entries for the given key
237
+
238
+ @param key TKey
239
+ @return Observable<number>
240
+ ]=]
313
241
  function ObservableMapSet:ObserveCountForKey(key)
314
242
  assert(key ~= nil, "Bad key")
315
243
 
@@ -321,66 +249,30 @@ function ObservableMapSet:ObserveCountForKey(key)
321
249
  })
322
250
  end
323
251
 
324
- function ObservableMapSet:_addToObservableSet(key, entry)
325
- local set = self:_getOrCreateObservableSet(key)
326
- set:Add(entry)
327
- end
328
-
329
- function ObservableMapSet:_removeFromObservableSet(key, entry)
330
- local set = self._observableSetMap[key]
331
- if not set then
332
- return
333
- end
334
-
335
- -- This happens when we're cleaning up sometimes
336
- if not set.Destroy then
337
- return
338
- end
339
-
340
- if set:Contains(entry) then
341
- set:Remove(entry)
342
- if set:GetCount() == 0 then
343
- self:_removeObservableSet(key)
344
- end
345
- end
346
- end
347
-
348
- function ObservableMapSet:_removeObservableSet(key)
349
- local set = self._observableSetMap[key]
350
- if set then
351
- self._observableSetMap[key] = nil
352
252
 
353
- -- Cleanup
354
- self._maid[set] = nil
355
-
356
- if self.SetRemoved.Destroy then
357
- self.SetRemoved:Fire(key)
358
- end
359
-
360
- if self._setCount.Destroy then
361
- self._setCount.Value = self._setCount.Value - 1
362
- end
363
- end
253
+ function ObservableMapSet:_addToSet(key, entry)
254
+ local set = self:_getOrCreateSet(key)
255
+ return set:Add(entry)
364
256
  end
365
257
 
366
- function ObservableMapSet:_getOrCreateObservableSet(key)
367
- if self._observableSetMap[key] then
368
- return self._observableSetMap[key]
258
+ function ObservableMapSet:_getOrCreateSet(key)
259
+ local existing = self._observableMapOfSets:Get(key)
260
+ if existing then
261
+ return existing
369
262
  end
370
263
 
371
264
  local maid = Maid.new()
372
- local set = ObservableSet.new()
373
- maid:GiveTask(set)
374
-
375
- self._observableSetMap[key] = set
265
+ local set = maid:Add(ObservableSet.new(nil))
376
266
 
377
- self.SetAdded:Fire(key, set)
378
-
379
- if self._setCount.Destroy then
380
- self._setCount.Value = self._setCount.Value + 1
381
- end
267
+ maid:GiveTask(set.CountChanged:Connect(function(count)
268
+ if count <= 0 then
269
+ self._maid[set] = nil
270
+ end
271
+ end))
382
272
 
273
+ maid:GiveTask(self._observableMapOfSets:Set(key, set))
383
274
  self._maid[set] = maid
275
+
384
276
  return set
385
277
  end
386
278
 
@@ -223,7 +223,7 @@ end
223
223
  @return { T }
224
224
  ]=]
225
225
  function ObservableSet:GetList()
226
- local list = {}
226
+ local list = table.create(self._countValue.Value)
227
227
  for item, _ in pairs(self._set) do
228
228
  table.insert(list, item)
229
229
  end
@@ -58,11 +58,8 @@ function ObservableSortedList.new(isReversed, compare)
58
58
 
59
59
  self._keyList = {} -- { [number]: Symbol } -- immutable
60
60
 
61
- self._indexObservers = ObservableSubscriptionTable.new()
62
- self._maid:GiveTask(self._indexObservers)
63
-
64
- self._contentIndexObservers = ObservableSubscriptionTable.new()
65
- self._maid:GiveTask(self._contentIndexObservers)
61
+ self._indexObservers = self._maid:Add(ObservableSubscriptionTable.new())
62
+ self._contentIndexObservers = self._maid:Add(ObservableSubscriptionTable.new())
66
63
 
67
64
  self._sortValue = {} -- { [Symbol]: number }
68
65
  self._contents = {} -- { [Symbol]: T }
@@ -127,6 +124,16 @@ function ObservableSortedList:Observe()
127
124
  })
128
125
  end
129
126
 
127
+ function ObservableSortedList:Contains(value)
128
+ -- TODO: Binary search
129
+ for _, item in pairs(self._contents) do
130
+ if item == value then
131
+ return true
132
+ end
133
+ end
134
+
135
+ return false
136
+ end
130
137
 
131
138
  --[=[
132
139
  Returns whether the value is an observable list
@@ -230,7 +237,7 @@ end
230
237
  @return Observable<number>
231
238
  ]=]
232
239
  function ObservableSortedList:ObserveIndexByKey(key)
233
- assert(type(key) == "userdata", "Bad key")
240
+ assert(Symbol.isSymbol(key), "Bad key")
234
241
 
235
242
  return Observable.new(function(sub)
236
243
  local maid = Maid.new()
@@ -289,9 +296,9 @@ end
289
296
  @return { T }
290
297
  ]=]
291
298
  function ObservableSortedList:GetList()
292
- local list = {}
293
- for _, key in pairs(self._keyList) do
294
- table.insert(list, self._contents[key])
299
+ local list = table.create(#self._keyList)
300
+ for index, key in pairs(self._keyList) do
301
+ list[index] = self._contents[key]
295
302
  end
296
303
  return list
297
304
  end
@@ -307,41 +314,27 @@ end
307
314
  --[=[
308
315
  Adds the item to the list at the specified index
309
316
  @param item T
310
- @param observeValue Observable<Comparable>
317
+ @param observeValue Observable<Comparable> | Comparable
311
318
  @return callback -- Call to remove
312
319
  ]=]
313
320
  function ObservableSortedList:Add(item, observeValue)
314
321
  assert(item ~= nil, "Bad item")
315
- assert(Observable.isObservable(observeValue), "Bad observeValue")
322
+ assert(Observable.isObservable(observeValue) or observeValue ~= nil, "Bad observeValue")
316
323
 
317
324
  local key = Symbol.named("entryKey")
318
325
  local maid = Maid.new()
319
326
 
320
327
  self._contents[key] = item
321
328
 
322
- maid:GiveTask(observeValue:Subscribe(function(sortValue)
323
- self:_debugVerifyIntegrity()
324
-
325
- if sortValue ~= nil then
326
- local currentIndex = self._indexes[key]
327
- local targetIndex = self:_findCorrectIndex(sortValue, currentIndex)
328
-
329
- self._sortValue[key] = sortValue
330
- self:_updateIndex(key, item, targetIndex, sortValue)
331
- else
332
- local observableSubs = self._keyObservables[key]
333
-
334
- -- calling this also may unsubscribe some observables.
335
- self:_removeItemByKey(key, item)
336
-
337
- if observableSubs then
338
- -- fire nil index
339
- self:_fireSubs(observableSubs, nil)
340
- end
341
- end
342
-
343
- self:_debugVerifyIntegrity()
344
- end))
329
+ if Observable.isObservable(observeValue) then
330
+ maid:GiveTask(observeValue:Subscribe(function(sortValue)
331
+ self:_assignSortValue(key, item, sortValue)
332
+ end))
333
+ elseif observeValue ~= nil then
334
+ self:_assignSortValue(key, item, observeValue)
335
+ else
336
+ error("Bad observeValue")
337
+ end
345
338
 
346
339
  maid:GiveTask(function()
347
340
  local observableSubs = self._keyObservables[key]
@@ -365,6 +358,31 @@ function ObservableSortedList:Add(item, observeValue)
365
358
  end
366
359
  end
367
360
 
361
+ function ObservableSortedList:_assignSortValue(key, item, sortValue)
362
+ self:_debugVerifyIntegrity()
363
+
364
+ if sortValue ~= nil then
365
+ local currentIndex = self._indexes[key]
366
+ local targetIndex = self:_findCorrectIndex(sortValue, currentIndex)
367
+
368
+ self._sortValue[key] = sortValue
369
+ self:_updateIndex(key, item, targetIndex, sortValue)
370
+ else
371
+ local observableSubs = self._keyObservables[key]
372
+
373
+ -- calling this also may unsubscribe some observables.
374
+ self:_removeItemByKey(key, item)
375
+
376
+ if observableSubs then
377
+ -- fire nil index
378
+ self:_fireSubs(observableSubs, nil)
379
+ end
380
+ end
381
+
382
+ self:_debugVerifyIntegrity()
383
+ end
384
+
385
+
368
386
  --[=[
369
387
  Gets the current item at the index, or nil if it is not defined.
370
388
  @param index number
@@ -446,11 +464,11 @@ function ObservableSortedList:_updateIndex(key, item, newIndex)
446
464
  error("Bad state")
447
465
  end
448
466
 
449
- local itemAdded = {
467
+ local itemAdded = table.freeze({
450
468
  key = key;
451
469
  newIndex = newIndex;
452
470
  item = item;
453
- }
471
+ })
454
472
 
455
473
  -- ensure ourself is considered changed
456
474
  table.insert(changed, itemAdded)
@@ -493,11 +511,11 @@ function ObservableSortedList:_removeItemByKey(key, item)
493
511
  end
494
512
  self._keyList[n] = nil
495
513
 
496
- local itemRemoved = {
514
+ local itemRemoved = table.freeze({
497
515
  key = key;
498
516
  item = item;
499
517
  previousIndex = index;
500
- }
518
+ })
501
519
 
502
520
  -- TODO: Defer item removed as a changed event?
503
521