根据数组值将数组项合并到新数组中

Merge array items into new array based on array value

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

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 循环,因为我还需要在将数据添加到这个最终数组之前异步处理数据 - 我明白制作新数组需要做些什么我一直在思考如何将其放入代码中。

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.reduce, Array.prototype.map and Object.keys 的 MDN 文档。

您可以在单个循环中完成此操作,并在散列上使用闭包 table。

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; }

试试运行这个代码:

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 数组获取。

你可以使用这个 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 应用于该地图。这会产生一个对数组(sub-arrays 带有键/值条目),但是 Array.from 接受一个回调函数,通过该函数可以将这个对转换为具有 2 个属性的对象。

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

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))