reducer内部的嵌套数组映射



有一个复杂的redux情况,我正在处理,无法完全得到我需要的东西。我有两个对象,TrackTarget

interface Track {
id: number,
...other fields
}
interface Target {
id: number (same as the Track)
tracks: Track[]
...other fields
}

我试图做的是,当我获取轨道时,我试图查看是否有任何目标具有相同的ID(一个目标对多个轨道(,如果有,则将该轨道添加到该目标的轨道阵列中,否则-使用该轨道ID创建一个新目标,并将该轨道放入轨道阵列中

case TargetActionTypes.FETCH_TARGETS_SUCCESS:
return {
...state,
targets: [
action.tracks.map((track: Track) =>
state.targets.map((target: Target) =>
track.target_id === target.id
? {
...target,
tracks: [...target.tracks, track]
}
: {
id: track.target_id,
visible: true,
tracks: [track]
}
)
)
]
}

我认为这是正确的,但typescript正在抱怨,因为我认为它的嵌套内容可能因为嵌套循环而太深了一层?此处出现打字错误

Types of property 'targets' are incompatible. Type '{ tracks: Track[]; id: number; visible: boolean; }[][][]' is not assignable to type 'Target[]'.
Type '{ tracks: Track[]; id: number; visible: boolean; }[][]' is missing the following properties from type 'Target': id, visible, tracks",

如注释中所述,您所在状态中的属性targets具有类型Target[],但您分配给它的不是。state.targets.map(...)返回一个目标数组,所以是Target[],然后它从回调函数返回到另一个map,所以action.tracks.map(...)返回Target的数组数组(所以Target[][](,然后你将它封装在一对方括号中,所以它变成了Target[][][]。对.map的调用返回一个数组,所以如果用方括号将其括起来,就会得到数组中的数组,所以第一步是删除这些数组。

现在,要去掉另一个级别的嵌套,您需要去掉.map,这在这种情况下不太起作用,因为.map只是将一个数组转换为另一个相同长度的数组。这里的长度可以变化,所以.map不是很适合。最简单的方法就是不使用内置方法并使用循环:

const targets: Target[] = []
for(const track of tracks) {
const target = targets.find(target => target.id === track.target_id)
if (target) target.push(track)
else targets.push({id: track.target_id, visible: true, tracks: [track]})
}

也许你可以通过在每次迭代中不遍历targets的整个数组来使这个算法更快一点

const targets: Target[] = []
const cache: Record<number, Target> = {}
for(const track of tracks) {
if(track.target_id in cache) {
cache[track.target_id].tracks.push(track)
} else {
targets.push(cache[track.target_id] = {
id: track.target_id, 
visible: true, 
tracks: [track]
})
}
}

如果你真的想要一些内置的方法,那么.reduce会更适合:

tracks.reduce<Target[]>(
(targets, track) => {
const target = targets.find(target => target.id === track.track_id)
if(!target) {
targets.push({id: track.target_id, visible: true, tracks: [track]})
} else {
target.tracks.push(track)
}
return targets

}
)

或者如果你不喜欢改变对象

tracks.reduce<Target[]>(
(targets, track) => {
const index = targets.findIndex(target => target.id === track.target_id)
if(index < 0) {
return [...targets, {id: track.target_id, visible: true, tracks: [track]}]
} 
return [
...targets.slice(0, index),
{...targets[index], tracks: [...targets[index].tracks, track]},
...targets.slice(index),
]
}
)

但我看不出这段代码有多大优势,因为它可读性不强,而且不必要地在数组之间多次复制元素

相关内容

  • 没有找到相关文章

最新更新