@quenty/observablecollection 12.11.1 → 12.12.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.
@@ -60,13 +60,12 @@ function ObservableSortedList.new(isReversed, compare)
60
60
 
61
61
  self._indexObservers = self._maid:Add(ObservableSubscriptionTable.new())
62
62
  self._contentIndexObservers = self._maid:Add(ObservableSubscriptionTable.new())
63
+ self._keyObservables = self._maid:Add(ObservableSubscriptionTable.new())
63
64
 
64
65
  self._sortValue = {} -- { [Symbol]: number }
65
66
  self._contents = {} -- { [Symbol]: T }
66
67
  self._indexes = {} -- { [Symbol]: number }
67
68
 
68
- self._keyObservables = {} -- { [Symbol]: { Subscription } }
69
-
70
69
  self._isReversed = isReversed or false
71
70
  self._compare = compare or defaultCompare
72
71
 
@@ -124,6 +123,19 @@ function ObservableSortedList:Observe()
124
123
  })
125
124
  end
126
125
 
126
+ --[=[
127
+ Allows iteration over the observable map
128
+
129
+ @return (T) -> ((T, nextIndex: any) -> ...any, T?)
130
+ ]=]
131
+ function ObservableSortedList:__iter()
132
+ return coroutine.wrap(function()
133
+ for index, value in self._keyList do
134
+ coroutine.yield(index, self._contents[value])
135
+ end
136
+ end)
137
+ end
138
+
127
139
  function ObservableSortedList:Contains(value)
128
140
  -- TODO: Binary search
129
141
  for _, item in pairs(self._contents) do
@@ -239,33 +251,16 @@ end
239
251
  function ObservableSortedList:ObserveIndexByKey(key)
240
252
  assert(Symbol.isSymbol(key), "Bad key")
241
253
 
242
- return Observable.new(function(sub)
243
- local maid = Maid.new()
244
- self._keyObservables[key] = self._keyObservables[key] or {}
245
- table.insert(self._keyObservables[key], sub)
246
-
247
- local currentIndex = self._indexes[key]
248
- if currentIndex then
249
- sub:Fire(currentIndex)
250
- end
251
-
252
- maid:GiveTask(function()
253
- local list = self._keyObservables[key]
254
- if not list then
255
- return
256
- end
257
-
258
- local index = table.find(list, sub)
259
- if index then
260
- table.remove(list, index)
261
- if #list == 0 then
262
- self._keyObservables[key] = nil
263
- end
254
+ return self._keyObservables:Observe(key):Pipe({
255
+ Rx.startFrom(function()
256
+ local currentIndex = self._indexes[key]
257
+ if currentIndex then
258
+ return { currentIndex }
259
+ else
260
+ return {}
264
261
  end
265
- end)
266
-
267
- return maid
268
- end)
262
+ end);
263
+ })
269
264
  end
270
265
 
271
266
  --[=[
@@ -337,15 +332,10 @@ function ObservableSortedList:Add(item, observeValue)
337
332
  end
338
333
 
339
334
  maid:GiveTask(function()
340
- local observableSubs = self._keyObservables[key]
341
- self._keyObservables[key] = nil
342
-
343
335
  self:_removeItemByKey(key, item)
344
336
 
345
337
  -- Fire off the index change on the value
346
- if observableSubs then
347
- self:_completeSubs(observableSubs)
348
- end
338
+ self._keyObservables:Complete(key)
349
339
 
350
340
  self._contents[key] = nil
351
341
  self._sortValue[key] = nil
@@ -359,7 +349,7 @@ function ObservableSortedList:Add(item, observeValue)
359
349
  end
360
350
 
361
351
  function ObservableSortedList:_assignSortValue(key, item, sortValue)
362
- self:_debugVerifyIntegrity()
352
+ -- self:_debugVerifyIntegrity()
363
353
 
364
354
  if sortValue ~= nil then
365
355
  local currentIndex = self._indexes[key]
@@ -368,18 +358,12 @@ function ObservableSortedList:_assignSortValue(key, item, sortValue)
368
358
  self._sortValue[key] = sortValue
369
359
  self:_updateIndex(key, item, targetIndex, sortValue)
370
360
  else
371
- local observableSubs = self._keyObservables[key]
372
-
373
361
  -- calling this also may unsubscribe some observables.
374
362
  self:_removeItemByKey(key, item)
375
-
376
- if observableSubs then
377
- -- fire nil index
378
- self:_fireSubs(observableSubs, nil)
379
- end
363
+ self._keyObservables:Complete(key)
380
364
  end
381
365
 
382
- self:_debugVerifyIntegrity()
366
+ -- self:_debugVerifyIntegrity()
383
367
  end
384
368
 
385
369
 
@@ -596,11 +580,7 @@ function ObservableSortedList:_queueDeferredChange()
596
580
  if self._indexes[lastChange.key] == lastChange.newIndex then
597
581
  changed = true
598
582
 
599
- local subs = self._keyObservables[lastChange.key]
600
- if subs then
601
- self:_fireSubs(subs, lastChange.newIndex)
602
- end
603
-
583
+ self._keyObservables:Fire(lastChange.key, lastChange.newIndex)
604
584
  self._indexObservers:Fire(lastChange.newIndex, self._contents[lastChange.key])
605
585
  end
606
586
  end
@@ -682,7 +662,9 @@ function ObservableSortedList:_lowBinarySearch(sortValue)
682
662
  while true do
683
663
  local mid = math.floor((minIndex + maxIndex) / 2)
684
664
  local compareValue = self._compare(self._sortValue[self._keyList[mid]], sortValue)
685
- assert(type(compareValue) == "number", "Expecting number")
665
+ if type(compareValue) ~= "number" then
666
+ error(string.format("Bad compareValue, expected number, got %q", type(compareValue)))
667
+ end
686
668
 
687
669
  if self._isReversed then
688
670
  compareValue = -compareValue
@@ -702,16 +684,6 @@ function ObservableSortedList:_lowBinarySearch(sortValue)
702
684
  end
703
685
  end
704
686
 
705
- function ObservableSortedList:_debugSortValuesToString()
706
- local values = {}
707
-
708
- for _, key in pairs(self._keyList) do
709
- table.insert(values, string.format("%4d", self._sortValue[key]))
710
- end
711
-
712
- return table.concat(values, ", ")
713
- end
714
-
715
687
  function ObservableSortedList:_debugVerifyIntegrity()
716
688
  for i=2, #self._keyList do
717
689
  local compare = self._compare(self._sortValue[self._keyList[i-1]], self._sortValue[self._keyList[i]])
@@ -730,23 +702,14 @@ function ObservableSortedList:_debugVerifyIntegrity()
730
702
  end
731
703
  end
732
704
 
733
- function ObservableSortedList:_fireSubs(list, index)
734
- for _, sub in pairs(list) do
735
- if sub:IsPending() then
736
- task.spawn(function()
737
- sub:Fire(index)
738
- end)
739
- end
740
- end
741
- end
705
+ function ObservableSortedList:_debugSortValuesToString()
706
+ local values = {}
742
707
 
743
- function ObservableSortedList:_completeSubs(list)
744
- for _, sub in pairs(list) do
745
- if sub:IsPending() then
746
- sub:Fire(nil)
747
- sub:Complete()
748
- end
708
+ for _, key in pairs(self._keyList) do
709
+ table.insert(values, string.format("%4d", self._sortValue[key]))
749
710
  end
711
+
712
+ return table.concat(values, ", ")
750
713
  end
751
714
 
752
715
  --[=[
@@ -0,0 +1,74 @@
1
+ --[[
2
+ @class ObservableSortedList.story
3
+ ]]
4
+
5
+ local require = require(game:GetService("ServerScriptService"):FindFirstChild("LoaderUtils", true).Parent).bootstrapStory(script)
6
+
7
+ local Maid = require("Maid")
8
+ local ObservableSortedList = require("ObservableSortedList")
9
+ local ObservableSortedListOld = require("ObservableSortedListOld")
10
+
11
+ return function(_target)
12
+ local maid = Maid.new()
13
+
14
+ local function test(n, label, sortedList, getElement)
15
+ local function add(number)
16
+ sortedList:Add(tostring(number), number)
17
+ end
18
+
19
+ local startTime = os.clock()
20
+ for i=1, n do
21
+ add(getElement(i))
22
+ end
23
+
24
+ print(string.format("%25s %0.2f ms", label .. " construction", (os.clock() - startTime)*1000))
25
+ end
26
+
27
+
28
+ local function cleanup(label, sortedList)
29
+ local startTime = os.clock()
30
+
31
+ sortedList:Destroy()
32
+
33
+ print(string.format("%25s %0.2f ms", label .. " destruction", (os.clock() - startTime)*1000))
34
+ end
35
+
36
+
37
+ local function getRandomElement()
38
+ return math.random()
39
+ end
40
+
41
+ local function inOrder(i)
42
+ return i
43
+ end
44
+
45
+ local function same()
46
+ return 0
47
+ end
48
+
49
+ local function runTest(label, n, getElement)
50
+ local observableSortedList = maid:Add(ObservableSortedList.new())
51
+ local observableSortedListOld = maid:Add(ObservableSortedListOld.new())
52
+
53
+ print(string.format("%25s n = %d", label, n))
54
+ print(string.format("%25s %8s", string.rep("-", 25), string.rep("-", 10)))
55
+
56
+ test(n, "prev impl", observableSortedListOld, getElement)
57
+ cleanup("prev impl", observableSortedListOld)
58
+
59
+ test(n, "new impl", observableSortedList, getElement)
60
+ cleanup("new impl", observableSortedList)
61
+
62
+
63
+ print("\n")
64
+ end
65
+
66
+ local n = 1000
67
+ runTest("test random_order", n, getRandomElement)
68
+ runTest("test in_order", n, inOrder)
69
+ runTest("same", n, same)
70
+
71
+ return function()
72
+ maid:DoCleaning()
73
+ end
74
+ end
@@ -0,0 +1,65 @@
1
+ --[[
2
+ @class ObservableSortedList.story
3
+ ]]
4
+
5
+ local require = require(game:GetService("ServerScriptService"):FindFirstChild("LoaderUtils", true).Parent).bootstrapStory(script)
6
+
7
+ local Maid = require("Maid")
8
+ local ObservableSortedList = require("ObservableSortedList")
9
+
10
+ return function(_target)
11
+ local maid = Maid.new()
12
+
13
+ print("----")
14
+
15
+ task.spawn(function()
16
+ local observableSortedList = maid:Add(ObservableSortedList.new())
17
+
18
+ local toRemove = {}
19
+
20
+ local function add(number)
21
+ table.insert(toRemove, observableSortedList:Add(tostring(number), number))
22
+ end
23
+
24
+ -- local random = Random.new(5000)
25
+ -- for i=1, 10 do
26
+ -- add(random:NextNumber())
27
+ -- end
28
+
29
+ local random = Random.new()
30
+ for _i=1, 10 do
31
+ add(math.floor(100*random:NextNumber()))
32
+ end
33
+
34
+ for index, node in observableSortedList._root:IterateNodesRange(3, 7) do
35
+ print(index, node:GetIndex())
36
+ end
37
+
38
+ observableSortedList:PrintDebug()
39
+
40
+ for _, item in toRemove do
41
+ item()
42
+ end
43
+
44
+ -- observableSortedList:PrintDebug()
45
+ observableSortedList:Destroy()
46
+ end)
47
+
48
+ -- for i=1, 10 do
49
+ -- add(-i)
50
+ -- add(i)
51
+ -- end
52
+
53
+ -- add(2)
54
+ -- add(1)
55
+ -- add(3)
56
+ -- add(4)
57
+ -- add(5)
58
+ -- add(0)
59
+
60
+ -- print(observableSortedList:GetList())
61
+
62
+ return function()
63
+ maid:DoCleaning()
64
+ end
65
+ end
@@ -0,0 +1,31 @@
1
+ --[=[
2
+ @class SortFunctionUtils
3
+ ]=]
4
+
5
+ local require = require(script.Parent.loader).load(script)
6
+
7
+ local SortFunctionUtils = {}
8
+
9
+ function SortFunctionUtils.reverse(compare)
10
+ compare = compare or SortFunctionUtils.default
11
+ return function(a, b)
12
+ return compare(b, a)
13
+ end
14
+ end
15
+
16
+ -- Higher numbers last
17
+ function SortFunctionUtils.default(a, b)
18
+ -- equivalent of `return a - b` except it supports comparison of strings and stuff
19
+ if b > a then
20
+ return -1
21
+ elseif b < a then
22
+ return 1
23
+ else
24
+ return 0
25
+ end
26
+ end
27
+
28
+ function SortFunctionUtils.emptyIterator()
29
+ end
30
+
31
+ return SortFunctionUtils