JavaScript-有没有办法通过指定的key属性合并2个对象?

时间:2018-08-03 22:01:03

标签: javascript node.js

JavaScript-是否可以通过指定的键属性合并2个对象?在这种情况下,关键属性是authorId

  

注意:作者3和作者1没有匹配项,因此不应将其合并。

var b = [{"book": "book1", "authorId": 3},{"book": "book2", "authorId":2}];

var a = [{"authorId": 1, "author": "author1"},{"authorId": 2, "author": "author2"}];

var c = a.merge(b);

console.log(c);

期望:

[{"book": "book2", "authorId": 2, "author": "author2"}]
  

我试图在这里完成SQL JOIN的工作。

https://jsfiddle.net/x67fpwoj/4/

3 个答案:

答案 0 :(得分:2)

map by Object.assign来自另一个数组中具有相同索引的项目:

var b = [{"book": "book1", "authorId": 1},{"book": "book2", "authorId":2}];
var a = [{"authorId": 1, "author": "author1"},{"authorId": 2, "author": "author2"}];
var c = a.map((aItem, i) => Object.assign({}, aItem, b[i]));
console.log(c);

如果不能指望每个要排序的数组,则可能reduce进入由authorId索引的对象,然后获取该对象的值(这具有O(N)的复杂性而不是每次迭代都使用.find(复杂度为O(N^2)):

var a = [{"authorId": 1, "author": "author1"},{"authorId": 2, "author": "author2"}];
var b = [{"book": "book1", "authorId": 1},{"book": "book2", "authorId":2}];

const reduceByAuthor = (arr, initial = {}) => arr.reduce((a, item) => {
  a[item.authorId] = Object.assign((a[item.authorId] || {}), item);
  return a;
}, initial);

const indexedByAuthor = reduceByAuthor(a);
reduceByAuthor(b, indexedByAuthor);
console.log(Object.values(indexedByAuthor));

如果您不能指望另一个数组中具有匹配项的每个项目,则reduce两次,第二个reduce仅在以下情况下分配给累加器:在第一个索引对象中找到匹配项(之后比filtering快):

var b = [{"book": "book1", "authorId": 3},{"book": "book2", "authorId":2}];
var a = [{"authorId": 1, "author": "author1"},{"authorId": 2, "author": "author2"}];

const aIndexed = a.reduce((a, item) => {
  a[item.authorId] = item;
  return a;
}, {});
const c = Object.values(b.reduce((a, bItem) => {
  const { authorId } = bItem;
  const aItem = aIndexed[authorId];
  if (aItem) a[authorId] = Object.assign({}, aItem, bItem);
  return a;
}, {}))

console.log(c);

答案 1 :(得分:1)

假设您需要指定要连接的字段(因为您提到了类似SQL的语法),而不必过于担心性能(如果这样,那么您将需要为要合并的对象建立索引,但请阅读然后,您可以使用几个数组方法和一个解构分配来合并对象数组。

const b = [{"book": "book1", "authorId": 3},{"book": "book2", "authorId":2}];

const a = [{"authorId": 1, "author": "author1"},{"authorId": 2, "author": "author2"}];

// Using reduce will allow filtering of unmatched records
const merge = (field, x, z) => x.reduce((out, v) => {
  // Make sure the field exists in the object to merge
  // Using find will return the first object that matches the criteria (multiple record matching was not mentioned as a concern in the OP)
  const match = v.hasOwnProperty(field) && z.find((c) => c[field] === v[field]);
  
  if (match) {
    // Only push onto the output array if there is a match
    // Use destructuring assignment to merge the matching objects
    out.push({...v, ...match});
  }

  return out;
}, []);

console.log(merge('authorId', a, b));

性能注意事项:出于多种原因,请谨慎使用索引方法。例如,当要合并的键的对象与要合并的数据集相比很小时。建立索引可能需要更长的时间,然后才在数组中搜索相对较少的匹配项。您应该在合理的情况下使用它们,但要注意,这样做不是一个简单的声明。

为完整起见,下面是带有临时索引的相同代码:

const b = [{"book": "book1", "authorId": 3},{"book": "book2", "authorId":2}];

const a = [{"authorId": 1, "author": "author1"},{"authorId": 2, "author": "author2"}];

const merge = (field, x, z) => {
  const index = z.reduce((out, v) => {
    if (v.hasOwnProperty(field)) {
      out[v[field]] = v;
    }
    
    return out;
  }, {});
  
  return x.reduce((out, v) => {
    const match = index[v[field]];

    if (match) {
      out.push({...v, ...match});
    }

    return out;
  }, []);
};

console.log(merge('authorId', a, b));

答案 2 :(得分:0)

我认为这将通过清晰的代码满足您的需求:

var c = [];
b.map(item1 => {
  a.map(item2 => {
    if (item1.authorId === item2.authorId) {
      c.push(Object.assign(item1, item2));
    }
  })
});
console.log(c);