2

I have a small problem that I am having some difficulty in identifying the best method of transforming the array. I have an array of objects like so:

[{
    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'],
}];

Using vanilla JS how best can I return a new array with items grouped by the "genre' property so I get the following result:

[{
    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'],
}];

I have been playing around with this in various for loops but I can't quite figure out a good solution.

6 Answers 6

2

One line of code answer:

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

Sign up to request clarification or add additional context in comments.

Comments

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

Comments

1

This is not terribly generic but it would work:

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

Comments

1

You can take advantage of Array.prototype.reduce to create a map where genre is the key and an array of books as the value. Then you can loop through the entries and map the results to a new array with object elements:

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

Comments

1

If you want to group them, the following code should work:

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

Comments

0

If we wanted to keep our changes to the array space itself, we could sort and iterate.

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.