0

I have the following source array:

const list = [
    {
        students: [ 'peter', 'bob', 'john']
    },
    {
        students: [ 'thomas', 'sarah', 'john']
    },
    {
        students: [ 'john', 'sarah', 'jack']
    }
];

and i want to get the unique student names and their count, final result should be like:

{
    'john': 3,
    'sarah': 2,
    'thomas': 1,
    'jack': 1,
    'peter': 1,
    'bob': 1
}

here is my attempt:

const unique = list.reduce(function(total, curr){
  const students = curr.students;
  for (c of students) {
    if (!total[c]) {
      total[c] = 1
    } else {
      total[c] += 1;
    }
  }
  return total;

}, {});

is there a better way to do it? or faster and cleaner way? thanks

2
  • Your expected output has the most frequently occurring names first in the object - is that required? (it's possible to achieve, just kinda odd to be a requirement, since a good script probably shouldn't depend on property order) Commented Sep 24, 2019 at 23:58
  • yeah i'd have it ordered too Commented Sep 24, 2019 at 23:59

2 Answers 2

2

I'd flatten the arrays first, then count up with reduce:

const list = [
    {
        students: [ 'peter', 'bob', 'john']
    },
    {
        students: [ 'thomas', 'sarah', 'john']
    },
    {
        students: [ 'john', 'sarah', 'jack']
    }
];


const allStudents = list.flatMap(({ students }) => students);
const count = allStudents.reduce((a, name) => {
  a[name] = (a[name] || 0) + 1;
  return a;
}, {});
console.log(count);

If you want the properties to be ordered as well, then take the Object.entries of the object, sort it, then turn it back into an object with Object.fromEntries:

const list = [
    {
        students: [ 'peter', 'bob', 'john']
    },
    {
        students: [ 'thomas', 'sarah', 'john']
    },
    {
        students: [ 'john', 'sarah', 'jack']
    }
];


const allStudents = list.flatMap(({ students }) => students);
const count = allStudents.reduce((a, name) => {
  a[name] = (a[name] || 0) + 1;
  return a;
}, {});
const sorted = Object.fromEntries(
  Object.entries(count).sort((a, b) => b[1] - a[1])
);
console.log(sorted);

If your environment doesn't support flatMap, or fromEntries, use a polyfill, or flatten/group with a different method:

const list = [
    {
        students: [ 'peter', 'bob', 'john']
    },
    {
        students: [ 'thomas', 'sarah', 'john']
    },
    {
        students: [ 'john', 'sarah', 'jack']
    }
];


const allStudents = [].concat(...list.map(({ students }) => students));
const count = allStudents.reduce((a, name) => {
  a[name] = (a[name] || 0) + 1;
  return a;
}, {});
const sortedEntries = Object.entries(count).sort((a, b) => b[1] - a[1]);
const sortedObj = sortedEntries.reduce((a, [prop, val]) => {
  a[prop] = val;
  return a;
}, {});
console.log(sortedObj);

Keep in mind that object property order is only specified in ES6+ environments. While Object.fromEntries isn't guaranteed by the specification to create an object in the same order as the entries, it does anyway, in any implementation I've ever encountered, luckily. (If you're still worried about it, you can use the old-fashioned reduce method to create the object instead, like in the third snippet)

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

3 Comments

TypeError: list.flatMap is not a function
Change it to [].concat(...list.map(({ students }) => students)) or list.map(({ students }) => students).flat().
@storis if list.flatMap is not a function, what browser or version of nodejs are you running?
0

Try using functional programming: combination of map and 2 reduce methods.

const listMapped = list.map(it=> it.students)

const listReduced = listMapped.reduce((acc, rec) => {
return [...acc.concat(rec)]
}, [])

const listCounted = listReduced.reduce((acc, rec) => {
acc[rec]
? acc[rec] += 1
: acc[rec] = 1
return acc 
}, {})

console.log(listCounted)

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.