如何按属性值聚合/分组对象数组?

时间:2019-07-29 22:44:33

标签: javascript algorithm

我有一个小问题,就是在确定转换数组的最佳方法方面有些困难。我有一个像这样的对象数组:

[{
    genre: 'fiction',
    books: ['book1', 'book2'],
},
{
    genre: 'fiction',
    books: ['book6', 'book51'],
},
{
    genre: 'non-fiction',
    books: ['book23', 'book34'],
},
{
    genre: 'fantasy',
    books: ['book241', 'book49'],
},
{
    genre: 'thriller',
    books: ['book67', 'book32'],
},
{
    genre: 'fantasy',
    books: ['book21', 'book99'],
}];

使用香草JS,如何最好地返回一个新数组,该数组具有按“ genre”属性分组的项目,因此得到以下结果:

[{
    genre: 'fiction',
    books: ['book1', 'book2', 'book6', 'book51'],
},
{
    genre: 'non-fiction',
    books: ['book23', 'book34'],
},
{
    genre: 'fantasy',
    books: ['book241', 'book49', 'book21', 'book99'],
},
{
    genre: 'thriller',
    books: ['book67', 'book32'],
}];

我一直在各种for循环中处理这个问题,但是我还不太想出一个好的解决方案。

6 个答案:

答案 0 :(得分:1)

这不是通用的,但可以使用:

var originalList = [{
    genre: 'fiction',
    books: ['book1', 'book2'],
},
{
    genre: 'fiction',
    books: ['book6', 'book51'],
},
{
    genre: 'non-fiction',
    books: ['book23', 'book34'],
},
{
    genre: 'fantasy',
    books: ['book241', 'book49'],
},
{
    genre: 'thriller',
    books: ['book67', 'book32'],
},
{
    genre: 'fantasy',
    books: ['book21', 'book99'],
}];

var newList = originalList.reduce((acc, val) => {
  let item = acc.find(i => i.genre === val.genre); // check if the genre is in the list
  if (item) {
    item.books = item.books.concat(val.books); // if so add the books and move on
    return acc;
  }
  val.books = val.books.slice(); // avoid mutation
  return acc.concat([Object.assign({}, val)]); // otherwise add the current item to the list and avoid mutation
},[]);

console.log(newList);

答案 1 :(得分:1)

var originalArray = [{
    genre: 'fiction',
    books: ['book1', 'book2'],
},
{
    genre: 'fiction',
    books: ['book6', 'book51'],
},
{
    genre: 'non-fiction',
    books: ['book23', 'book34'],
},
{
    genre: 'fantasy',
    books: ['book241', 'book49'],
},
{
    genre: 'thriller',
    books: ['book67', 'book32'],
},
{
    genre: 'fantasy',
    books: ['book21', 'book99'],
}];

var newArray = [];

for(var i = 0; i< originalArray.length; i++){
  var idx = newArray.findIndex(x => x.genre === originalArray[i].genre);
  if(idx < 0){
    newArray.push(originalArray[i]);
  } else {
    var newBooks = newArray[idx].books.concat(originalArray[i].books);
    newArray[idx].books = newBooks;
  }
}

console.log(newArray);

答案 2 :(得分:1)

一行代码答案:

const input = [{
    genre: 'fiction',
    books: ['book1', 'book2'],
  },
  {
    genre: 'fiction',
    books: ['book6', 'book51'],
  },
  {
    genre: 'non-fiction',
    books: ['book23', 'book34'],
  },
  {
    genre: 'fantasy',
    books: ['book241', 'book49'],
  },
  {
    genre: 'thriller',
    books: ['book67', 'book32'],
  },
  {
    genre: 'fantasy',
    books: ['book21', 'book99'],
  }
]

// the code
const output = Object.entries(input.reduce((a, {genre, books}) => (a[genre] = (a[genre] || []).concat(books), a), {})).map(([genre, books]) => ({genre, books}));
// end of code
console.log(output);

答案 3 :(得分:1)

您可以利用Array.prototype.reduce来创建一个地图,其中类型是关键,而书本则是价值。然后,您可以遍历条目并将结果映射到带有对象元素的新数组:

const arr = [{
    genre: 'fiction',
    books: ['book1', 'book2'],
  },
  {
    genre: 'fiction',
    books: ['book6', 'book51'],
  },
  {
    genre: 'non-fiction',
    books: ['book23', 'book34'],
  },
  {
    genre: 'fantasy',
    books: ['book241', 'book49'],
  },
  {
    genre: 'thriller',
    books: ['book67', 'book32'],
  },
  {
    genre: 'fantasy',
    books: ['book21', 'book99'],
  }
];

const agg = Object.entries(arr.reduce((accum, el) => {
  accum[el.genre] = accum[el.genre] ? [...accum[el.genre], ...el.books] : [...el.books]
  return accum;
}, {})).map(entry => {
  const [genre, books] = entry;
  return {
    [genre]: books
  };
})

console.log(agg);

答案 4 :(得分:1)

如果要对它们进行分组,则以下代码应该起作用:

var json = [{
    genre: 'fiction',
    books: ['book1', 'book2'],
},
{
    genre: 'fiction',
    books: ['book6', 'book51'],
},
{
    genre: 'non-fiction',
    books: ['book23', 'book34'],
},
{
    genre: 'fantasy',
    books: ['book241', 'book49'],
},
{
    genre: 'thriller',
    books: ['book67', 'book32'],
},
{
    genre: 'fantasy',
    books: ['book21', 'book99'],
}];
function groupGenres(arrayOfObjects){
  var genres = [], g;
  arrayOfObjects.forEach(function(j){
    g = false;
    genres.forEach(function(o, i){
      if(o.genre === j.genre)g = i;
    });
    if(g === false){
      genres.push(j);
    }
    else{
      genres[g].books = genres[g].books.concat(j.books);
    }
  });
  return genres;
}
var group = groupGenres(json);
console.log(group);

答案 5 :(得分:0)

如果我们想保留对数组空间本身的更改,则可以进行排序和迭代。

function merge(A, prop, group){
  A.sort((a, b) => a[prop] > b[prop] ? 1 : -1)
  let key = A[0][prop]
  let pointer = 1
  for (let i=1; i<A.length; i++){
    if (A[i][prop] != key){
      A[++pointer - 1] = A[i]
      key = A[i][prop]
    } else {
      A[pointer - 1][group].push(...A[i][group])
    }
  }
  A.length = pointer
  return A
}

var A = [{genre: 'fiction', books: ['book1', 'book2']}, {genre: 'fiction', books: ['book6', 'book51']}, {genre: 'non-fiction', books: ['book23', 'book34']}, {genre: 'fantasy', books: ['book241', 'book49']}, {genre: 'thriller', books: ['book67', 'book32']}, {genre: 'fantasy', books: ['book21', 'book99']}]

merge(A, 'genre', 'books')
console.log(JSON.stringify(A))