简体   繁体   中英

How to filter an array of objects and merge them into a single object?

I need to convert the following array of objects into a an object format when topic = 'favorites'.

Original Format

const events = [
      {
        topic: 'favorites',
        receiver: '1000066928',
        datetime: 2016-11-26T21:43:54.000Z
      },
      {
        topic: 'favorites',
        receiver: '1000061499',
        datetime: 2017-01-03T19:15:04.000Z
      },
      {
        topic: 'blocked',
        receiver: '1000102733',
        datetime: 2017-01-05T17:04:15.000Z
      },
      {
        topic: 'blocked',
        receiver: '1000107928',
        datetime: 2017-01-06T17:17:05.000Z
      }
    ]

Converted Format

{
    1000000002: '2020-01-24T20:46:05+11:00',
    1000000008: '2020-01-24T20:46:05+11:00',
    1000000009: '2020-01-24T20:46:05+11:00',
    1000000010: '2020-01-24T20:46:05+11:00'
},

I've been trying to work with the following but I'm a bit off.

events.filter((event) => event.topic === "favorites").map(receiver, datetime => receiver: datetime)

You could filter and map the entries for a new object.

 const events = [{ topic: 'favorites', receiver: '1000066928', datetime: '2016-11-26T21:43:54.000Z' }, { topic: 'favorites', receiver: '1000061499', datetime: '2017-01-03T19:15:04.000Z' }, { topic: 'blocked', receiver: '1000102733', datetime: '2017-01-05T17:04:15.000Z' }, { topic: 'blocked', receiver: '1000107928', datetime: '2017-01-06T17:17:05.000Z' }], result = Object.fromEntries(events.filter(({ topic }) => topic === 'favorites').map(({ receiver, datetime }) => [receiver, datetime]) ); console.log(result);

Your given data doesn't correspond to your expected output by value . But I assume the format is correct. It also seems like, in your result, you want the dates in ISO string form. But in your source object they are kept as a Date object. So I'll be assuming the source array does indeed contain Date objects.

With that said, you really just have to turn every object into [receiver, datetime] (after filtering) and do Object.fromEntries on the result

const result = Object.fromEntries(
    events
        .filter(event => event.topic === 'favorites')
        .map(({ receiver, datetime }) => [receiver, datetime.toISOString()])
);

 const events = [ { topic: 'favorites', receiver: '1000066928', datetime: new Date('2016-11-26T21:43:54.000Z'), }, { topic: 'favorites', receiver: '1000061499', datetime: new Date('2017-01-03T19:15:04.000Z'), }, { topic: 'blocked', receiver: '1000102733', datetime: new Date('2017-01-05T17:04:15.000Z'), }, { topic: 'blocked', receiver: '1000107928', datetime: new Date('2017-01-06T17:17:05.000Z'), }, ]; const result = Object.fromEntries( events.filter(event => event.topic === 'favorites').map(({ receiver, datetime }) => [receiver, datetime.toISOString()]) ); console.log(result);

With reduce you can built an object that will be updated in every iteration.

const output = events
  .filter((event) => event.topic === "favorites")
  .reduce((acc, cur) => {
    return {...acc, [cur.receiver]: cur.datetime };
  }, {});
console.log(output);
/* {
  '1000061499': '2017-01-03T19:15:04.000Z',
  '1000066928': '2016-11-26T21:43:54.000Z'
}
*/

you can use reduce and filter for this in combination

I had to do a string out of datetime otherwise it would result in an error.

 const events = [ { topic: 'favorites', receiver: '1000066928', datetime: "2016-11-26T21:43:54.000Z" }, { topic: 'favorites', receiver: '1000061499', datetime: "2017-01-03T19:15:04.000Z" }, { topic: 'blocked', receiver: '1000102733', datetime: "2017-01-05T17:04:15.000Z" }, { topic: 'blocked', receiver: '1000107928', datetime: "2017-01-06T17:17:05.000Z" } ] const result = events.filter((event) => event.topic === "favorites").reduce((accum, element) => { return {...accum, [element.receiver]: element.datetime }; }, {}); console.log(result);

You can use reduce like this:

const filteredEvents = events.reduce((a, b) => {
  return b.topic === 'favorites' 
    ? {...a, [b.receiver] : b.datetime} 
    : a
}, {})

console.log(filteredEvents);

/*
{
  '1000061499': '2017-01-03T19:15:04.000Z',
  '1000066928': '2016-11-26T21:43:54.000Z'
}
*/

This is a classic use case for using the .reduce() prototype function on the array. This will save you multiple repeated iterations with fewer lines of code.

The example data doesn't seem to match but I am assuming the format is correct.

In your case, you can use reduce like this -

const events = [
    {
        topic: 'favorites',
        receiver: '1000066928',
        datetime: new Date('2016-11-26T21:43:54.000Z'),
    },
    {
        topic: 'favorites',
        receiver: '1000061499',
        datetime: new Date('2017-01-03T19:15:04.000Z'),
    },
    {
        topic: 'blocked',
        receiver: '1000102733',
        datetime: new Date('2017-01-05T17:04:15.000Z'),
    },
    {
        topic: 'blocked',
        receiver: '1000107928',
        datetime: new Date('2017-01-06T17:17:05.000Z'),
    },
];

const result = events.reduce((acc, event) => {
  if (b.topic === 'favorites') {
    return {...acc, [event.receiver] : b.datetime};
  }
  return acc;
}, {})

console.log(result);

To better understand how reduce works - https://www.freecodecamp.org/news/reduce-f47a7da511a9/

A one line reducer should be sufficient

 const events = [{ topic: 'favorites', receiver: '1000066928', datetime: "2016-11-26T21:43:54.000Z" }, { topic: 'favorites', receiver: '1000061499', datetime: "2017-01-03T19:15:04.000Z" }, { topic: 'blocked', receiver: '1000102733', datetime: "2017-01-05T17:04:15.000Z" }, { topic: 'blocked', receiver: '1000107928', datetime: "2017-01-06T17:17:05.000Z" } ] console.log(events.reduce((acc, val) => val.topic === "favorites" && {...acc, [val.receiver]: val.datetime } || acc, {}));

A single reduce combined with a topic filter should be enough and some answers have already demonstrated how to do this.

This answer does that but adds:

  1. Currying. So you can apply the transformation to other topics
  2. The comma operator instead of the spread operator. Depending on how big the list to process is, the spread operator can quickly become a performance bottleneck
const transform =
  topic => xs =>
    xs.reduce( (o, x) =>
                  (x.topic === topic
                    ? (o[x.receiver] = x.datetime, o)
                    : o), {});

What's going on here?

The function is curried; it takes a topic then a list to process. This means you can build two topic-processing functions:

const favorites = transform('favorites');
const blocked = transform('blocked');

These two functions now only wait for a list to be processed. Assuming list is the same as your events array:

favorites(list);
// { 1000061499: "2017-01-03T19:15:04.000Z"
// , 1000066928: "2016-11-26T21:43:54.000Z"
// }

blocked(list);
// { 1000102733: "2017-01-05T17:04:15.000Z"
// , 1000107928: "2017-01-06T17:17:05.000Z"
// }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM