Summarize count of occurrences in an array of objects with Array#reduce

天大地大妈咪最大 提交于 2019-12-14 04:19:06

问题


I want to summarize an array of objects and return the number of object occurrences in another array of objects. What is the best way to do this?

From this

var arrayOfSongs = [
  {"title":"Blue","duration":161.71,"audioUrl":"/assets/music/blue","playing":false,"playedAt":"2016-12-21T22:58:55.203Z"},
  {"title":"Blue","duration":161.71,"audioUrl":"/assets/music/blue","playing":false,"playedAt":"2016-12-21T22:58:55.203Z"},
  {"title":"Blue","duration":161.71,"audioUrl":"/assets/music/blue","playing":false,"playedAt":"2016-12-21T22:58:55.203Z"},
  {"title":"Green","duration":161.71,"audioUrl":"/assets/music/blue","playing":false,"playedAt":"2016-12-21T22:58:55.203Z"}
];

To this

var newArrayOfSongs = [
  {"title": "Blue", "playCount": 3 },
  {"title": "Green", "playCount": 1}
]

I have tried

 arrayOfSongs.reduce(function(acc, cv) {
   acc[cv.title] = (acc[cv.title] || 0) + 1;
     return acc;
   }, {});
 }

But it returns an object:

 { "Blue": 3, "Green": 1};

回答1:


You should pass the initial argument to the reduce function as an array instead of object and filter array for the existing value as below,

Working snippet:

var arrayOfSongs = [
  {"title":"Blue","duration":161.71,"audioUrl":"/assets/music/blue","playing":false,"playedAt":"2016-12-21T22:58:55.203Z"},
  {"title":"Blue","duration":161.71,"audioUrl":"/assets/music/blue","playing":false,"playedAt":"2016-12-21T22:58:55.203Z"},
  {"title":"Blue","duration":161.71,"audioUrl":"/assets/music/blue","playing":false,"playedAt":"2016-12-21T22:58:55.203Z"},
  {"title":"Green","duration":161.71,"audioUrl":"/assets/music/blue","playing":false,"playedAt":"2016-12-21T22:58:55.203Z"}
];


var newArrayOfSongs = arrayOfSongs.reduce(function(acc, cv) {
    var arr = acc.filter(function(obj) {
      return obj.title === cv.title;
    });
   
    if(arr.length === 0) {
      acc.push({title: cv.title, playCount: 1});
    } else {
      arr[0].playCount += 1;
    }
    
    return acc;
   }, []);

console.log(newArrayOfSongs);



回答2:


To build on what you already have done, the next step is to "convert" the object to an array

    var arrayOfSongs = [
        {"title":"Blue","duration":161.71,"audioUrl":"/assets/music/blue","playing":false,"playedAt":"2016-12-21T22:58:55.203Z"},
        {"title":"Blue","duration":161.71,"audioUrl":"/assets/music/blue","playing":false,"playedAt":"2016-12-21T22:58:55.203Z"},
        {"title":"Blue","duration":161.71,"audioUrl":"/assets/music/blue","playing":false,"playedAt":"2016-12-21T22:58:55.203Z"},
        {"title":"Green","duration":161.71,"audioUrl":"/assets/music/blue","playing":false,"playedAt":"2016-12-21T22:58:55.203Z"}
    ];

    var obj = arrayOfSongs.reduce(function(acc, cv) {
       acc[cv.title] = (acc[cv.title] || 0) + 1;
       return acc;
    }, {});

    // *** added code starts here ***
    var newArrayOfSongs = Object.keys(obj).map(function(title) { 
        return {
            title: title, 
            playCount:obj[title]
        };
    });

    console.log(newArrayOfSongs);



回答3:


I recommend doing this in two stages. First, chunk the array by title, then map the chunks into the output you want. This will really help you in future changes. Doing this all in one pass is highly complex and will increase the chance of messing up in the future.

var arrayOfSongs = [
  {"title":"Blue","duration":161.71,"audioUrl":"/assets/music/blue","playing":false,"playedAt":"2016-12-21T22:58:55.203Z"},
  {"title":"Blue","duration":161.71,"audioUrl":"/assets/music/blue","playing":false,"playedAt":"2016-12-21T22:58:55.203Z"},
  {"title":"Blue","duration":161.71,"audioUrl":"/assets/music/blue","playing":false,"playedAt":"2016-12-21T22:58:55.203Z"},
  {"title":"Green","duration":161.71,"audioUrl":"/assets/music/blue","playing":false,"playedAt":"2016-12-21T22:58:55.203Z"}
];

function chunkByAttribute(arr, attr) {
  return arr.reduce(function(acc, e) {
   acc[e[attr]] = acc[e[attr]] || [];
   acc[e[attr]].push(e);
   return acc;
  }, {});
}

var songsByTitle = chunkByAttribute(arrayOfSongs, 'title');

var formattedOutput = Object.keys(songsByTitle).map(function (title) {
  return {
    title: title,
    playCount: songsByTitle[title].length
  };
});

There, now everything is named according to what it does, everything does just one thing, and is a bit easier to follow.




回答4:


https://jsfiddle.net/93e35wcq/

I used a set object to get the unique track titles, then used Array.map to splice those and return a song object that contains play count inside the track title.

The Data:

var arrayOfSongs = [{
  "title": "Blue",
  "duration": 161.71,
  "audioUrl": "/assets/music/blue",
  "playing": false,
  "playedAt": "2016-12-21T22:58:55.203Z"
}, {
  "title": "Blue",
  "duration": 161.71,
  "audioUrl": "/assets/music/blue",
  "playing": false,
  "playedAt": "2016-12-21T22:58:55.203Z"
}, {
  "title": "Blue",
  "duration": 161.71,
  "audioUrl": "/assets/music/blue",
  "playing": false,
  "playedAt": "2016-12-21T22:58:55.203Z"
}, {
  "title": "Green",
  "duration": 161.71,
  "audioUrl": "/assets/music/blue",
  "playing": false,
  "playedAt": "2016-12-21T22:58:55.203Z"
}];

The Function:

function getPlayCount(arrayOfSongs) {
  let songObj = {};
  let SongSet = new Set();
  arrayOfSongs.map(obj => (SongSet.has(obj.title)) ? true : SongSet.add(obj.title));
  for (let songTitle of SongSet.values()) {
    songObj[songTitle] = {
      playCount: 0
    };
    arrayOfSongs.map(obj => (obj.title === songTitle) ? songObj[songTitle].playCount++ : false)
  }
  return songObj;
}

console.log(getPlayCount(arrayOfSongs));

Which isn't exactly what you wanted formatting wise, but if you're married to it, this will do the trick:

    function getPlayCount(arrayOfSongs) {
  let songObj = {};
  let SongSet = new Set();
  arrayOfSongs.map(obj => (SongSet.has(obj.title)) ? true : SongSet.add(obj.title));
  for (let songTitle of SongSet.values()) {
    songObj[songTitle] = 0;
    arrayOfSongs.map(obj => (obj.title === songTitle) ? songObj[songTitle]++ : false)
  }
  return songObj;
}

console.log(getPlayCount(arrayOfSongs));

https://jsfiddle.net/93e35wcq/1/



来源:https://stackoverflow.com/questions/41273917/summarize-count-of-occurrences-in-an-array-of-objects-with-arrayreduce

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!