mirror of
https://github.com/littensy/charm.git
synced 2026-10-22 12:24:08 +00:00
fix: remove old keys when updating key in mapped()
This commit is contained in:
parent
81d547ae82
commit
e426e76ff1
2 changed files with 64 additions and 29 deletions
|
|
@ -874,62 +874,76 @@ end
|
||||||
of an existing atom. If the key is omitted from the mapper's return value,
|
of an existing atom. If the key is omitted from the mapper's return value,
|
||||||
the original key is preserved.
|
the original key is preserved.
|
||||||
|
|
||||||
|
This function is optimized to minimize unnecessary updates by only calling
|
||||||
|
the mapper for entries that have changed since the last run.
|
||||||
|
|
||||||
@param getter A signal or function that returns a table
|
@param getter A signal or function that returns a table
|
||||||
@param mapper Function that maps each entry from the source to a new entry in the mapped table
|
@param mapper Function that maps each entry from the source to a new entry in the mapped table
|
||||||
@see https://github.com/littensy/charm?tab=readme-ov-file#mappedgetter-mapper
|
@see https://github.com/littensy/charm?tab=readme-ov-file#mappedgetter-mapper
|
||||||
]]
|
]]
|
||||||
local function mapped<KI, VI, KO, VO>(getter: Getter<{ [KI]: VI }>, mapper: (VI, KI) -> (VO, KO)): Getter<{ [KO]: VO }>
|
local function mapped<KI, VI, KO, VO>(getter: Getter<{ [KI]: VI }>, mapper: (VI, KI) -> (VO, KO)): Getter<{ [KO]: VO }>
|
||||||
local currentInput: { [KI]: VI } = {}
|
local nextInput: { [KI]: VI } = {}
|
||||||
local keyCache: { [KI]: KO } = {}
|
local keyMap: { [KI]: KO } = {}
|
||||||
|
|
||||||
local function map(value: VI, key: KI): (VO, KO)
|
local function callMapper(value: VI, key: KI): (VO, KO)
|
||||||
local prevSub = setActiveSub(nil)
|
local prevSub = setActiveSub(nil)
|
||||||
local success, valueOut, keyOut = pcall(mapper, value, key)
|
local success, valueOut, keyOut = pcall(mapper, value, key)
|
||||||
activeSub = prevSub
|
activeSub = prevSub
|
||||||
if not success then
|
if not success then
|
||||||
error(`mapped() transform function failed: {valueOut}`)
|
error(`mapped() transform function failed: {valueOut}`)
|
||||||
end
|
end
|
||||||
return valueOut, if keyOut == nil then key else keyOut :: any
|
return valueOut, if keyOut == nil then key :: any else keyOut
|
||||||
end
|
end
|
||||||
|
|
||||||
return computed(function(previousOutput)
|
return computed(function(lastOutput)
|
||||||
local currentOutput: { [KO]: VO } = previousOutput or {}
|
local nextOutput = lastOutput or {}
|
||||||
local previousInput = currentInput
|
local prevInput = nextInput
|
||||||
currentInput = getter()
|
nextInput = getter()
|
||||||
|
|
||||||
for keyIn in previousInput do
|
-- For keys that were removed from the input, remove the corresponding
|
||||||
if currentInput[keyIn] ~= nil then
|
-- keys from the output.
|
||||||
continue
|
for prevKeyIn in prevInput do
|
||||||
end
|
if nextInput[prevKeyIn] == nil then
|
||||||
|
local prevKeyOut = keyMap[prevKeyIn]
|
||||||
local prevKeyOut = keyCache[keyIn]
|
if prevKeyOut ~= nil then
|
||||||
|
if nextOutput == lastOutput then
|
||||||
if prevKeyOut then
|
nextOutput = table.clone(nextOutput)
|
||||||
if currentOutput == previousOutput then
|
end
|
||||||
currentOutput = table.clone(currentOutput)
|
nextOutput[prevKeyOut] = nil
|
||||||
|
keyMap[prevKeyIn] = nil
|
||||||
end
|
end
|
||||||
currentOutput[prevKeyOut] = nil
|
|
||||||
keyCache[keyIn] = nil
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
for keyIn, valueIn in currentInput do
|
-- For keys that are new or changed, pass it to the mapper and update
|
||||||
if valueIn == previousInput[keyIn] then
|
-- the output. If the mapper returns a new key that is different from
|
||||||
|
-- the previous call, the old mapped key should be removed from the
|
||||||
|
-- output.
|
||||||
|
for nextKeyIn, nextValueIn in nextInput do
|
||||||
|
if nextValueIn == prevInput[nextKeyIn] then
|
||||||
continue
|
continue
|
||||||
end
|
end
|
||||||
|
|
||||||
local valueOut, keyOut = map(valueIn, keyIn)
|
local nextValueOut, nextKeyOut = callMapper(nextValueIn, nextKeyIn)
|
||||||
|
local prevKeyOut = keyMap[nextKeyIn]
|
||||||
|
|
||||||
if currentOutput[keyOut] ~= valueOut then
|
if prevKeyOut ~= nil and prevKeyOut ~= nextKeyOut then
|
||||||
if currentOutput == previousOutput then
|
if nextOutput == lastOutput then
|
||||||
currentOutput = table.clone(currentOutput)
|
nextOutput = table.clone(nextOutput)
|
||||||
end
|
end
|
||||||
currentOutput[keyOut] = valueOut
|
nextOutput[prevKeyOut] = nil
|
||||||
keyCache[keyIn] = keyOut
|
nextOutput[nextKeyOut] = nextValueOut
|
||||||
|
keyMap[nextKeyIn] = nextKeyOut
|
||||||
|
elseif nextOutput[nextKeyOut] ~= nextValueOut then
|
||||||
|
if nextOutput == lastOutput then
|
||||||
|
nextOutput = table.clone(nextOutput)
|
||||||
|
end
|
||||||
|
nextOutput[nextKeyOut] = nextValueOut
|
||||||
|
keyMap[nextKeyIn] = nextKeyOut
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return currentOutput
|
return nextOutput
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -104,4 +104,25 @@ test("should map keys", function()
|
||||||
assert(state.D == 4, "should add d")
|
assert(state.D == 4, "should add d")
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
test("should remove old key if mapper returns a new key", function()
|
||||||
|
local src, setSrc = signal({ 1 })
|
||||||
|
local map = mapped(src, function(value: number, index: number)
|
||||||
|
return true, `{index}={value}`
|
||||||
|
end)
|
||||||
|
|
||||||
|
local state = map()
|
||||||
|
assert(state["1=1"], "should have mapped first element to key 1=1")
|
||||||
|
|
||||||
|
setSrc({ 2 })
|
||||||
|
state = map()
|
||||||
|
assert(state["1=1"] == nil, "should have removed old key 1=1")
|
||||||
|
assert(state["1=2"], "should have remapped element to key 1=2")
|
||||||
|
|
||||||
|
setSrc({ 3, 1 })
|
||||||
|
state = map()
|
||||||
|
assert(state["1=2"] == nil, "should have removed old key 1=2")
|
||||||
|
assert(state["1=3"], "should have remapped element to key 1=3")
|
||||||
|
assert(state["2=1"], "should have mapped second element to key 2=1")
|
||||||
|
end)
|
||||||
|
|
||||||
return {}
|
return {}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue