【问题标题】:merge objects in same array based on common value in Javascript [duplicate]基于Javascript中的公共值合并同一数组中的对象[重复]
【发布时间】:2017-05-02 15:37:11
【问题描述】:

我有一个数组:

[
  {
    assignmentId:17,
    email:"john.smith@email.com"
    expectation: "Make sure to proofread!",
    firstName:"John"
    id:23
    ignoreForFeedback: true
    lastName:"Smith"
    level:2
    levelFraction:null
    score:35
  },
  {
    assignmentId:17
    countsPerCategory: Array(4)
    email:"john.smith@email.com"
    firstName:"John"
    frequentErrors: Array(5)
    id:23
    ignoreForGrading: true
    lastName:"Smith"
  },
  {
    assignmentId:17,
    email:"cl@email.com"
    expectation: "cite sources",
    firstName:"Cindy"
    id:45
    ignoreForFeedback: true
    lastName:"Lee"
    level:2
    levelFraction:null
    score:32
  },
  {
    assignmentId:17
    countsPerCategory: Array(4)
    email:"cl@email.com"
    firstName:"Cindy"
    frequentErrors: Array(5)
    id:45
    ignoreForGrading: true
    lastName:"Lee"
  }
]

我想将具有相同“id”的对象组合到数组中的同一个对象中。它们的公共键也应该组合起来(例如:'firstName'、'email')。有人可以建议最好的方法吗?使用 ES6 或 Lodash

【问题讨论】:

  • 看看我的解决方案是不是你想要的

标签: javascript arrays lodash


【解决方案1】:

您可以使用lodash#groupBy 将数组中的所有项目按id 分组,然后将lodash#maplodash#assign 的迭代对象一起使用lodash#spread 包装,以使数组回调作为一个列表lodash#assgin 的参数。

var result = _(array)
  .groupBy('id')
  .map(_.spread(_.assign))
  .value();

var array = [
  {
    assignmentId:17,
    email:"john.smith@email.com",
    expectation: "Make sure to proofread!",
    firstName:"John",
    id:23,
    ignoreForFeedback: true,
    lastName:"Smith",
    level:2,
    levelFraction:null,
    score:35
  },
  {
    assignmentId:17,
    countsPerCategory: Array(4),
    email:"john.smith@email.com",
    firstName:"John",
    frequentErrors: Array(5),
    id:23,
    ignoreForGrading: true,
    lastName:"Smith"
  },
  {
    assignmentId:17,
    email:"cl@email.com",
    expectation: "cite sources",
    firstName:"Cindy",
    id:45,
    ignoreForFeedback: true,
    lastName:"Lee",
    level:2,
    levelFraction:null,
    score:32
  },
  {
    assignmentId:17,
    countsPerCategory: Array(4),
    email:"cl@email.com",
    firstName:"Cindy",
    frequentErrors: Array(5),
    id:45,
    ignoreForGrading: true,
    lastName:"Lee"
  }
];

var result = _(array)
  .groupBy('id')
  .map(_.spread(_.assign))
  .value();
  
console.log(result);
body > div { min-height: 100%; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>

这是使用Array#filter 的替代解决方案,它利用Array#filter 的第二个参数,为过滤器的回调函数提供上下文。我们使用this 上下文作为一种机制,通过它们的id 存储缓存的对象,然后使用它来决定是否从数组中保留这些对象。

var result = array.filter(function(v) {
  return this[v.id]?
    !Object.assign(this[v.id], v):
    (this[v.id] = v);
}, {});

var array = [
  {
    assignmentId:17,
    email:"john.smith@email.com",
    expectation: "Make sure to proofread!",
    firstName:"John",
    id:23,
    ignoreForFeedback: true,
    lastName:"Smith",
    level:2,
    levelFraction:null,
    score:35
  },
  {
    assignmentId:17,
    countsPerCategory: Array(4),
    email:"john.smith@email.com",
    firstName:"John",
    frequentErrors: Array(5),
    id:23,
    ignoreForGrading: true,
    lastName:"Smith"
  },
  {
    assignmentId:17,
    email:"cl@email.com",
    expectation: "cite sources",
    firstName:"Cindy",
    id:45,
    ignoreForFeedback: true,
    lastName:"Lee",
    level:2,
    levelFraction:null,
    score:32
  },
  {
    assignmentId:17,
    countsPerCategory: Array(4),
    email:"cl@email.com",
    firstName:"Cindy",
    frequentErrors: Array(5),
    id:45,
    ignoreForGrading: true,
    lastName:"Lee"
  }
];

var result = array.filter(function(v) {

  // does this `id` exist?
  return this[v.id]? 
  
    // assign existing object with the same id
    // from the `this` cache object. Make sure
    // to negate the resulting object with a `!`
    // to remove this value from the array
    !Object.assign(this[v.id], v):
    
    // Assign the value from the `this` cache.
    // This also retains this value from the existing
    // array
    (this[v.id] = v);
    
}, {});

console.log(result);
body > div { min-height: 100%; top: 0; }

【讨论】:

  • @dedles 谢谢,我还添加了一个 vanilla javascript 作为替代解决方案。如果您找到了自己喜欢的整体解决方案,那么您可以将其标记为已接受的答案。
【解决方案2】:

您可以使用 JavaScript 内置的 Array.reduce() 方法。这个想法是您可以使用 ID 创建一个映射并使用 lodash.merge() 方法(或您选择用于合并对象的任何方法)将具有相同 ID 的所有对象合并为一个对象。然后您可以在您创建的idMap 上使用.map() 将对象重新放入单个数组中。

    var data = [{
        assignmentId: 17,
        email: "john.smith@email.com",
        expectation: "Make sure to proofread!",
        firstName: "John",
        id: 23,
        ignoreForFeedback: true,
        lastName: "Smith",
        level: 2,
        levelFraction: null,
        score: 35
      },
      {
        assignmentId: 17,
        countsPerCategory: Array(4),
        email: "john.smith@email.com",
        firstName: "John",
        frequentErrors: Array(5),
        id: 23,
        ignoreForGrading: true,
        lastName: "Smith"
      },
      {
        assignmentId: 17,
        email: "cl@email.com",
        expectation: "cite sources",
        firstName: "Cindy",
        id: 45,
        ignoreForFeedback: true,
        lastName: "Lee",
        level: 2,
        levelFraction: null,
        score: 32
      },
      {
        assignmentId: 17,
        countsPerCategory: Array(4),
        email: "cl@email.com",
        firstName: "Cindy",
        frequentErrors: Array(5),
        id: 45,
        ignoreForGrading: true,
        lastName: "Lee"
      }
    ];

    var idMap = data.reduce(function(result, current) {
      if (result[current.id] == null) {
        result[current.id] = current;
      } else {
        _.merge(result[current.id], current);
      }

      return result;
    }, {});

    var results = Object.keys(idMap).map(function(key) {
      return idMap[key];
    });

    console.log(results);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>

【讨论】:

    【解决方案3】:

    我可以建议使用forEach()some() 方法的组合来迭代数组元素并测试迭代的对象ID 是否已经被处理。

    这是解决方案:

    var merged = [];
    
    arr.forEach(function(item) {
      var idx;
      var found = merged.some(function(el, i) {
        idx = el.id === item.id ? i : null;
        return el.id === item.id;
      });
      if (!found) {
        merged.push(item);
      } else if (idx !== null) {
        for (k in Object.keys(item)) {
          if (item.hasOwnProperty(k)) {
            merged[idx][k] = item[k];
          }
        }
      }
    });
    

    工作演示:

    var arr = [{
        assignmentId: 17,
        email: "john.smith@email.com",
        expectation: "Make sure to proofread!",
        firstName: "John",
        id: 23,
        ignoreForFeedback: true,
        lastName: "Smith",
        level: 2,
        levelFraction: null,
        score: 35
      },
      {
        assignmentId: 17,
        countsPerCategory: [],
        email: "john.smith@email.com",
        firstName: "John",
        frequentErrors: [],
        id: 23,
        ignoreForGrading: true,
        lastName: "Smith"
      },
      {
        assignmentId: 17,
        email: "cl@email.com",
        expectation: "cite sources",
        firstName: "Cindy",
        id: 45,
        ignoreForFeedback: true,
        lastName: "Lee",
        level: 2,
        levelFraction: null,
        score: 32
      },
      {
        assignmentId: 17,
        countsPerCategory: [],
        email: "cl@email.com",
        firstName: "Cindy",
        frequentErrors: [],
        id: 45,
        ignoreForGrading: true,
        lastName: "Lee"
      }
    ];
    var merged = [];
    
    arr.forEach(function(item) {
      var idx;
      var found = merged.some(function(el, i) {
        idx = el.id === item.id ? i : null;
        return el.id === item.id;
      });
      if (!found) {
        merged.push(item);
      } else if (idx !== null) {
        for (k in Object.keys(item)) {
          if (item.hasOwnProperty(k)) {
            merged[idx][k] = item[k];
          }
        }
      }
    });
    console.log(merged);

    【讨论】:

      【解决方案4】:

      感谢大家的帮助,但我最终还是选择了自己的实现。

              let ids = [];
              let combinedUsers = [];
      
              users.forEach(function (user) {
                  ids.push(user.id);
              });
      
              ids = _.uniq(ids);
              ids.forEach(function(id){
                  let user = users.filter(function(userObj){
                      return id === userObj.id
                  });
      
                  if(user.length > 1){
                      user = Object.assign(user[0], user[1]);
                      combinedUsers.push(user);
                  } else {
                      combinedUsers.push(user[0]);
                  }
      
              });
              return combinedStudents;
      

      【讨论】:

      • 嗯,使用.filter() 方法是个好主意,但是您的解决方案主要基于我的,您至少可以投票赞成,但无论如何您不应该将其发布为答案,它应该是对您的问题的编辑。 :)
      • 也许伟大的思想是一样的?在我注意到你的回答之前,我已经写了它。不过,为了你的麻烦,我支持你。
      • 不,我只是想说,如果您将答案发布为问题的编辑会更好。就是这样。
      猜你喜欢
      • 1970-01-01
      • 2023-02-13
      • 2021-03-29
      • 1970-01-01
      • 2019-03-26
      • 2019-06-11
      • 1970-01-01
      • 2016-10-29
      • 1970-01-01
      相关资源
      最近更新 更多