假设我有两个数组:
let A = [a,b,c,d]
let B = [c,d,e]
其中每个字母是一个具有多个属性的对象,其中一个是id
,在我的域中是唯一的。合并后的结果将是
[a,b,c,d,e]
[c,d]
来自B。
使用一种优雅的方法,我将如何合并这两个数组,使得B
中的任何元素都将覆盖A
中的任何现有元素,而所有其他元素都保持不变。因此,这是一个B
元素在出现冲突时优先的联合。
我有两个想法(使用ES6和lodash):
//remove same elements first, then join arrays
let ids = new Set(B.map(e => e.id));
let newState = _.reject(A, constraint => ids.has(constraint.id));
return newState.concat(B);
//convert both to hashmap, join, then take values
let B_map = _.keyBy(B, 'id');
let A_map = _.keyBy(A, 'id');
return {...A_map, ...B_map}.values();
是否有一个更短/更简洁/更易读的版本?也许一个没有外部依赖的人?我本质上是在寻找
其中任何元素之间的相等性均由id
属性(或v2中的比较器函数)定义。
答案 0 :(得分:3)
没有外部依赖性,您可以使用filter
从A
中提取在B
中没有ID的元素,而在concat
中使用B
来删除ID:
const A = [{id: 1, name: 'x'}, {id: 2, name: 'y'}, {id: 3, name: 'z'}];
const B = [{id: 2, name: 'hello'}];
let ids = new Set(B.map(e => e.id));
let newState = A.filter(a => !ids.has(a.id)).concat(B);
console.log(newState);
答案 1 :(得分:3)
由于您已经在使用lodash,所以可以使用_.unionBy
,它使用计算唯一性的标准合并数组:
let result = _.unionBy(B, A, "id");
从B
之前的A
开始,以便在重复的情况下使用B
的值,而不是A
的值。
示例:
let A = [
{ id: "a", arr: "A" },
{ id: "b", arr: "A" },
{ id: "c", arr: "A" },
{ id: "d", arr: "A" }
];
let B = [
{ id: "b", arr: "B" },
{ id: "d", arr: "B" }
];
let result = _.unionBy(B, A, "id");
console.log(result);
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.11/lodash.min.js"></script>
注意:这会弄乱项目的顺序,重复项排在第一位,然后是其余部分。
答案 2 :(得分:0)
使用lodash的_.differenceBy(A, B)
从B
中删除A
中存在的所有项目,然后与B
项合并。这将保留A项在B项之前的顺序。
const A = [{"id":"a","arr":"A"},{"id":"b","arr":"A"},{"id":"c","arr":"A"},{"id":"d","arr":"A"}];
const B = [{"id":"c","arr":"B"},{"id":"d","arr":"B"}];
const result = [..._.differenceBy(A, B, 'id'), ...B];
console.log(result);
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.11/lodash.min.js"></script>
答案 3 :(得分:0)
这是一个遵循更多redux样式方法的解决方案...
// imported redux actions (these are simply strings)
import {
SOME_DEFINED_ACTION_CASE
} from '../actions/someActions';
const initialState = {
reduxList: []
}
// reducer function
export default function someReducer(state = initialState, action) {
switch (action.type) {
case SOME_DEFINED_ACTION_CASE: {
let ids = new Set(action.payload.map(e => e.id));
let newState = state.reduxList.filter(a => !ids.has(a.id)).concat(action.payload);
return Object.assign({}, state, {
reduxList: newState
});
}
}
}