3

I have an array of arrays. How do I convert them to an array of objects grouped together?

var abc = [
  ['apple', 'fruit'], ['banana', 'fruit'], ['grapes', 'fruit'], ['red', 'color'], ['blue', 'color'], ['black', 'color']
]


abc = abc.map(function(item) {
  return {
    value: item[0],
    color: item[1]
  }
})

colors = abc.filter(function(item) {
  return item.color === 'color'
})

fruits = abc.filter(function(item) {
  return item.color === 'fruit'
})

var result = [{
  group: 'color',
  value: colors.map(function(item) {
    return item.value
  })
}, {
  group: 'fruit',
  value: fruits.map(function(item) {
    return item.value
  })
}]

console.log(result)

My expected output is:

var abc = [
{
    group: 'color',
    value: ['red', 'blue', 'black']
},
{
    group: 'fruit',
    value: ['apple', 'banana', 'grapes']
}]

Is there an easier way to achieve this?

I can use lodash too. Please advise.

1
  • Use reduce, group elements that match the group value; by extending object IF already exists, and creating if it does not. Commented Oct 4, 2018 at 19:08

6 Answers 6

4

Here's an approach using reduce and map. reduce performs an aggregation by group mapped to an array of values. Object.entries() and map transform this grouping into the desired format as an array of objects.

var abc = [
  ['apple', 'fruit'],
  ['banana', 'fruit'],
  ['grapes', 'fruit'],
  ['red', 'color'],
  ['blue', 'color'],
  ['black', 'color']
];

const result = Object.entries(
    abc.reduce((a, e) => {
      if (!(e[1] in a)) {
        a[e[1]] = [];
      }

      a[e[1]].push(e[0]);
      return a;
    }, {})
  ).map(e => ({group: e[0], value: e[1]}))
;

console.log(result);

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

1 Comment

IMHO this is cleanest and most "functional" approach to this problem.
2

You could group with lodash by the index and map the values of the other index.

var data = [['apple', 'fruit'], ['banana', 'fruit'], ['grapes', 'fruit'], ['red', 'color'], ['blue', 'color'], ['black', 'color']],
    result = _(data)
        .groupBy(1)
        .map((array, group) => ({ group, value: _.map(array, 0) }))
        .sortBy('group')
        .value();

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.15.0/lodash.min.js"></script>

Comments

1

Here's a method I've described in a comment to your question:

  1. you loop through all elements, and create a blank array
  2. Inside of your newly created array, you find object matching group value
  3. If found, you extend the element
  4. If not found, you create it

    abc.reduce((arr, input) => {
      const index = arr.findIndex(i => i.group === input[0])
    
      return [
        ...(index > -1
          ? [
            ...arr.slice(0, index),
            {
              group: input[0],
              value: [
                ...arr[index].value,
                input[1]
              ]
            },
            ...arr.slice(index + 1)
          ] : [
            ...arr,
            {
              group: input[0],
              value: [ input[1] ]
            }
          ])
      ]
    }, [])
    

2 Comments

IMHO such long ternary operator may be confusing to decrypt, but hey, that's at least different approach
@Tomas agree, but that's why consistent indentation is the key here
1

Here is way to do it with an ES6 Map as intermediate data structure:

var abc = [['apple', 'fruit'],['banana', 'fruit'],['grapes', 'fruit'],['red', 'color'],['blue', 'color'],['black', 'color']];
    
const groups = new Map(abc.map(a =>[a[1], []]));
abc.forEach(a => groups.get(a[1]).push(a[0]));
const result = Array.from(groups, ([group, values]) =>  ({group, values}));

console.log(result);

Comments

0

You could build up a hashtavle to find duplicate descriptors:

 const result = [], hash = {};

 for(const [value, group] of abc) {
  if(hash[group]) {
   hash[group].push(value);
  } else {
   result.push({ group, value: (hash[group] = [value]), });
  }
}

Comments

0

Approach with single reduce

var abc = [
  ['apple', 'fruit'],
  ['banana', 'fruit'],
  ['grapes', 'fruit'],
  ['red', 'color'],
  ['blue', 'color'],
  ['black', 'color']
]

var r = abc.reduce((prev, curr) => {
  if(prev.findIndex(p => p.group === curr[1]) === -1) {
    prev = prev.concat({group: curr[1], value: []})
  }
  let idx = prev.findIndex(p => p.group === curr[1])
  prev[idx].value.push( curr[0] )
  return prev;
}, [])

console.log(r)

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.