【问题标题】:Merge array items into new array based on array value根据数组值将数组项合并到新数组中
【发布时间】:2017-07-08 08:30:30
【问题描述】:

我有一个包含故事的数组,每个故事也有一个“日”,我希望能够将同一“日”的故事合并到一个新数组中。

stories = [
    {
        id: 1,
        day: '18-02-2017',
        user: 1,
        story_data: //JSON containing a single story + other data
    },
    {
        id: 2,
        day: '18-02-2017',
        user: 1,
        story_data: //JSON containing a single story + other data
    },
    {
        id: 3,
        day: '17-02-2017',
        user: 1,
        story_data: //JSON containing a single story + other data
    }

]

这是我希望输出数组的样子:

feed = [
    {
        day: '18-02-2017',
        stories: [
            //multiple JSON story items
        ]
    },
    {
        day: '17-02-2017',
        stories: [
            //multiple JSON story items
        ]
    }

]

我将 NodeJS 中的 Async 库用于必要的 FOR 循环,因为在将数据添加到这个最终数组之前,我还需要异步处理数据 - 我了解需要发生什么才能使新数组刚刚开始完全不知道如何将其放入代码中。

【问题讨论】:

  • 用户id值不重要?
  • @trincot 不是,我只是想证明数组中还有其他数据。我想我可以把它排除在外。

标签: javascript arrays json async.js


【解决方案1】:

你可以使用这个 ES6 代码:

const result = Array.from(
    stories.reduce( 
        (acc, {day, story_data}) => acc.set(day, (acc.get(day) || []).concat(story_data)),
        new Map
    ),
    ([day, story_data]) => ({day, story_data})
);

const stories = [
    {
        id: 1,
        day: '18-02-2017',
        user: 1,
        story_data: "story1"
    },
    {
        id: 2,
        day: '18-02-2017',
        user: 1,
        story_data: "story2"
    },
    {
        id: 3,
        day: '17-02-2017',
        user: 1,
        story_data: "story3"
    }
];

const result = Array.from(
    stories.reduce( 
        (acc, {day, story_data}) => acc.set(day, (acc.get(day) || []).concat(story_data)),
        new Map
    ),
    ([day, story_data]) => ({day, story_data})
);

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

说明

reduce 方法创建一个Map,以日期为键,并将相应的故事数组作为值。因此,如果该键在地图中尚不存在,则取一个空数组(|| []),否则取其当前值,然后将新故事添加到其中。

reduce 返回的映射本身就是一个很好的结构,但是当您要求对象数组时,Array.from 将应用于该映射。这会产生一个对数组(具有键/值条目的子数组),但Array.from 接受一个回调函数,通过该函数可以将该对转换为具有 2 个属性的对象。

此解决方案使用箭头函数、解构和Map,这些都是 ES6 特性。

【讨论】:

  • :-) 是的,括号和大括号太多了。像 Lisp 一样,哈哈。但是你也喜欢它——我也从你那里看到了一些非常类似于 lamda 的答案:P
  • 是否有一些风格指南?
  • 我想应该按照大括号的规则放置括号。我稍微更新了格式。
【解决方案2】:
let day2story = {};

stories.forEach((story) => {
  let curr = day2story[story.day] || [];
  curr.push(story.story_data);
  day2story[story.day] = curr;
});

let feed = [];

Object.keys(day2story).forEach((day) => feed.push({day: day, stories: day2story[day]}));

console.log(JSON.stringify(feed))

【讨论】:

    【解决方案3】:
    function groupByDay(arr) {
      var hash = arr.reduce(function(h, s) {                   // h is the hash object, s is the current story
        h[s.day] = h[s.day] || {'day': s.day, 'stories': []};  // if the hash doesn't have an entry for this story's day, then add it
        h[s.day].stories.push(s);                              // add this story to the stories array of the object that acummulates the result for this day
        return h;
      }, {});
    
      return Object.keys(hash).map(function(key) {             // unwrap the objects from the hash object and return them as an array (the result)
        return hash[key];
      });
    }
    

    这里是 Array.prototype.reduceArray.prototype.mapObject.keys 的 MDN 文档。

    【讨论】:

      【解决方案4】:

      尝试运行此代码:

      var jsonStories = {};
      stories.forEach(function(story) {
          if (!jsonStories[story['day']]) {
              jsonStories[story['day']] = {
                  'day': story['day'],
                  'stories': []
              };
          }
          jsonStories[story['day']]['stories'].push(story);
      });
      var storiesArr = [];
      Object.keys(jsonStories).forEach(function(key) {
          storiesArr.push(jsonStories[key]);
      });
      

      你会得到一个有序的数组。 如果删除最后四行,也可以将其作为 JSON 数组获取。

      【讨论】:

        【解决方案5】:

        您可以在单个循环中使用散列表上的闭包来完成。

        var stories = [{ id: 1, day: '18-02-2017', user: 1, story_data: '01' }, { id: 2, day: '18-02-2017', user: 1, story_data: '02' }, { id: 3, day: '17-02-2017', user: 1, story_data: '03' }],
            result = stories.reduce(function (hash) {
                return function (r, a) {
                    if (!hash[a.day]) {
                        hash[a.day] = { day: a.day, stories: [] };
                        r.push(hash[a.day]);
                    }
                    hash[a.day].stories.push(a);
                    return r;
                };
            }(Object.create(null)), []);
        
        console.log(result);
        .as-console-wrapper { max-height: 100% !important; top: 0; }

        【讨论】:

        • 尼娜,天哪,这太棒了。如果你想描述一下你在这里做了什么......
        • @Kinduser,它使用一个对象和day 作为键和一个对象作为值,其中故事被收集在stories 属性中。这是哈希表的一部分。另一部分是在数组中获得想要的输出。这是用一个数组处理的,并检查哈希表中是否存在这一天。如果没有,则生成 hast 表对象并将相同的对象推送到结果数组。这使对收集的故事和哈希表的引用保持在当天。当您现在有了一天的哈希值时,您可以将故事添加到其中。瞧。
        • 谢谢。但是,我没有再明白一件事,添加if (!hash[a.day]){} 有什么意义,它有什么作用?
        • @Kinduser,它检查哈希表中是否存在这一天......我上面写的。
        • 我明白了。谢谢。顺便说一句——你的答案就像一条永无止境的知识之河。您应该打印它们并像书籍或其他东西一样出售xd
        猜你喜欢
        • 2014-10-14
        • 2021-07-16
        • 2018-07-22
        • 1970-01-01
        • 1970-01-01
        • 2016-11-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多