0

I have a set of data like this:

[
  {
    name:'Bart',
    classes:['Maths','Philosophy','Music']
  },
  {
    name:'Lisa',
    classes: ['Maths','Literature','Music']
  },
  {
    name:'Maggie',
    classes: ['Quantum Physics','Literature']
  }
]

And I would like to output an array like

[ 
  { "name": "Maths", "count": 2 },
  { "name": "Music", "count": 2 },
  { "name": "Literature", "count": 2 },
  { "name": "Quantum Physics", "count": 1 },
  { "name": "Philosophy", "count": 1 }
]

What would be the optimal way to do it in ES6? Thanks in advance!

Edit: my attempt

    let mappedArray = Array.map(item => item.classes)
    let flattenedArray = mappedArray.reduce(
      ( accumulator, currentValue ) => accumulator.concat(currentValue),
      []
    );
    let countedArray = flattenedArray.reduce(function (allClasses, item) {
      if (item in allClasses) {
        allClasses[item]++;
      }
      else {
        allClasses[item] = 1;
      }
      return allClasses;
    }, {});
    return countedArray
3
  • 2
    What do you think is the optimal way to do this? Please post a minimal reproducible example of your attempt and say specifically why you don't think it's the optimal way. Commented Aug 22, 2018 at 10:38
  • 2
    please also share your attempt Commented Aug 22, 2018 at 10:41
  • Edited with my attempt Commented Aug 22, 2018 at 11:02

5 Answers 5

4

You can use Array.reduce() with forEach() loop inside reduce() to loop over the classes array:

var arr = [{
    name: 'Bart',
    classes: ['Maths', 'Philosophy', 'Music']
  },
  {
    name: 'Lisa',
    classes: ['Maths', 'Literature', 'Music']
  },
  {
    name: 'Maggie',
    classes: ['Quantum Physics', 'Literature']
  }
];

var res = arr.reduce((acc, obj) => {
  var classes = obj.classes;
  classes.forEach((subject) => {
      var exist = acc.find(({name}) => subject === name);
      if (exist) {
        exist.count++;
      } else {
        acc.push({
          name: subject,
          count: 1
        });
      }
  });
  return acc;
}, []);
console.log(res);

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

Comments

2

Using Array reduce I have first created a temporary Object which looks:

{
  "Maths": 2,
  "Philosophy": 1,
  "Music": 2,
  "Literature": 2,
  "Quantum Physics": 1
}

Then I have looped over the temporaryObject to generate the desired output

var originalData = [
  {
    name:'Bart',
    classes:['Maths','Philosophy','Music']
  },
  {
    name:'Lisa',
    classes: ['Maths','Literature','Music']
  },
  {
    name:'Maggie',
    classes: ['Quantum Physics','Literature']
  }
]

var tempCount = originalData.reduce((acc, elem) => {
  elem.classes.forEach((eachClass) => {
    if (eachClass in acc) {
      acc[eachClass] ++;
    }
    else {
      acc[eachClass] = 1;
    }
  })
  return acc
}, {});

console.log(tempCount)

var desiredResult = [];
for (var eachSub in tempCount) {
  if (tempCount.hasOwnProperty(eachSub)) {
    desiredResult.push({name: eachSub, count: tempCount[eachSub] })
  }
}

console.log(desiredResult)

Comments

1

Try this code:

    var names=[],
    counts=[],
    output=[],
    input=[
      {
        name:'Bart',
        classes:['Maths','Philosophy','Music']
      },
      {
        name:'Lisa',
        classes: ['Maths','Literature','Music']
      },
      {
        name:'Maggie',
        classes: ['Quantum Physics','Literature']
      }
    ]
    for(i in input){
        for(j in input[i].classes){
            if (!names.includes(input[i].classes[j])){
                names.push(input[i].classes[j])
                counts.push(0)
            }
            counts[names.indexOf(input[i].classes[j])]++
        }
    }
    for(k in names){
        output.push({name:names[k],count:counts[k]})
    }
    console.log(output)

Comments

1

You can use reduce & concat

let x = [{
    name: 'Bart',
    classes: ['Maths', 'Philosophy', 'Music']
  },
  {
    name: 'Lisa',
    classes: ['Maths', 'Literature', 'Music']
  },
  {
    name: 'Maggie',
    classes: ['Quantum Physics', 'Literature']
  }
]

let m = x.reduce(function(acc, curr) {
 // this will return an array of 'Maths', 'Philosophy', 'Music'...] 
 // & it will have duplicate elements
  return acc.concat(curr.classes)
}, []).reduce(function(acc, curr) {
  // again using reduce function to return an object key as subject name & count
  if (!acc.hasOwnProperty(curr)) {
    acc[curr] = 1
  } else {
    acc[curr] += 1
  }
  return acc;
}, {});
console.log(m)

Comments

0

you can do :

let data = [
  {
    name:'Bart',
    classes:['Maths','Philosophy','Music']
  },
  {
    name:'Lisa',
    classes: ['Maths','Literature','Music']
  },
  {
    name:'Maggie',
    classes: ['Quantum Physics','Literature']
  }
]

let result = {}

for (let i of data) i.classes.forEach(j => {if (!result[j]) result[j]=0; result[j]++})

console.log(result)

it may not be optimal but seems quite good


same but more human readable

for (let student of data) {
  student.classes.forEach(clas => {
    if (!result[clas]) {
      result[clas]=0
    }
    result[clas]++
  })
}

// using "clas" because "class" is a reserved javascript word

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.