这是使用 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(注意列表是一个休息参数)。
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 方法基本相同,只是它保留一个索引对象来标识要加入的记录。记录存储在数组中,索引存储给定键集的记录位置以及在其中找到的列表数。
每次遇到相同的键集时,它都会在树中找到节点,更新其索引处的元素,并递增找到它的次数。
加入后, idx 对象从带有切片的数组中删除,并且在每个集合中找不到的任何元素都将被删除。这使它成为内连接,您可以删除此过滤器并拥有完整的外连接。
最后,每个元素都映射到它的值,并且您有连接的数组。