这里是一个使用 reduce 和 Object.assign 的 O(n) 解决方案
const joinById = ( ...lists ) =>
Object.values(
lists.reduce(
( idx, list ) => {
list.forEach( ( record ) => {
if( idx[ record.id ] )
idx[ record.id ] = Object.assign( idx[ record.id ], record)
else
idx[ record.id ] = record
} )
return idx
},
{}
)
)
要在 OP 的情况下使用此函数,请将您想要拼接的数组传递给 joinById(请注意,lists 是一个 rest 参数)。
let joined = joinById(list1, list2)
每个列表都被缩减为一个单一的对象,其中键是ID,而值是对象。如果给定的键已经有一个值,它将调用object.assign进行合并当前记录。
这里是通用的O(n*m)解决方案,其中n是记录数,m是键数。这仅适用于有效的对象键。如果需要,您可以将任何值转换为base64并使用它。
const join = ( keys, ...lists ) =>
lists.reduce(
( res, list ) => {
list.forEach( ( record ) => {
let hasNode = keys.reduce(
( idx, key ) => idx && idx[ record[ key ] ],
res[ 0 ].tree
)
if( hasNode ) {
const i = hasNode.i
Object.assign( res[ i ].value, record )
res[ i ].found++
} else {
let node = keys.reduce( ( idx, key ) => {
if( idx[ record[ key ] ] )
return idx[ record[ key ] ]
else
idx[ record[ key ] ] = {}
return idx[ record[ key ] ]
}, res[ 0 ].tree )
node.i = res[ 0 ].i++
res[ node.i ] = {
found: 1,
value: record
}
}
} )
return res
},
[ { i: 1, tree: {} } ]
)
.slice( 1 )
.filter( node => node.found === lists.length )
.map( n => n.value )
这与joinById方法本质上相同,不同之处在于它保留了一个索引对象以标识要连接的记录。记录存储在数组中,索引存储给定关键字集的记录位置和它所在列表的数量。
每次遇到相同的关键字集时,它会查找树中的节点,更新其索引处的元素,并增加已找到的次数。
连接后,使用slice从数组中删除idx对象并删除每个集合中未找到的任何元素。这使它成为内部连接,您可以删除此过滤器并进行完全外部连接。
最后,将每个元素映射到其值,并得到了连接的数组。
array1.map(x => { return array2.map(y => { if (y.id === x.id) { x.date = y.date; return x; } }); });
注意:我尽量保持原文意思不变,同时使翻译更通俗易懂。 - Thadeus Ajayi