合并两个数组,避免在redux中使用不可变模式重复

时间:2018-12-12 17:14:02

标签: javascript ecmascript-6 redux lodash

假设我有两个数组:

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中的比较器函数)定义。

4 个答案:

答案 0 :(得分:3)

没有外部依赖性,您可以使用filterA中提取在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
            });
        }
    }
}