有一个复杂的redux情况,我正在处理,无法完全得到我需要的东西。我有两个对象,Track
和Target
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),
]
}
)
但我看不出这段代码有多大优势,因为它可读性不强,而且不必要地在数组之间多次复制元素