Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

308
Views
Using reduce, how to make an object directory where every key is a unique value and stores repeated values as sub-objects?

How do I create a function using reduce which will take in an array of objects and return an object directory with unique keys, but allowing for repeated values?

  1. each key is the name of the person
  2. each repeated key has a nested array with a directory of numbers
  3. if there are no repeated keys, the key should holds only one object

I would like the output to look like this:

{
  Bob: {
    '0': { name: 'Bob', age: 30, voted: true },
    '1': { name: 'Bob', age: 31, voted: true },
    '2': { name: 'Bob', age: 32, voted: true },
       },
  Jake: {
    '0': { name: 'Jake', age: 20, voted: false },
    '1': { name: 'Jake', age: 32, voted: true }
       },
  Phil: { name: 'Phil', age: 21, voted: true },
  Ed: { name: 'Ed', age: 55, voted: true },
  Tami: { name: 'Tami', age: 54, voted: true },
  Mary: { name: 'Mary', age: 31, voted: false },
  Becky: { name: 'Becky', age: 43, voted: false },
  Joey: { name: 'Joey', age: 41, voted: true },
  Jeff: { name: 'Jeff', age: 30, voted: true },
  Zack: { name: 'Zack', age: 19, voted: false }
}

This is my code, the results eat one another if two repeated names happen, and it spreads the first object into the key without adding a number key to it.

let count = 0
const toNamedObj = (arr) => {
  return arr.reduce((acc, cur) => {
    let key = Object.values(cur)[0]
    if(acc.hasOwnProperty(key)){
      count ++
      console.log(count)
      return {[key]: {...acc[key], [count]: cur} }
    }
    return {...acc, [key]: cur}
  } ,{})
}

const voters = [
  {name:'Bob' , age: 30, voted: true},
  {name:'Bob' , age: 31, voted: true},
  {name:'Bob' , age: 32, voted: true},
  {name:'Jake' , age: 32, voted: true},
  {name:'Kate' , age: 25, voted: false},
  {name:'Jake' , age: 20, voted: false},
  {name:'Phil' , age: 21, voted: true},
  {name:'Ed' , age:55, voted:true},
  {name:'Tami' , age: 54, voted:true},
  {name: 'Mary', age: 31, voted: false},
  {name: 'Becky', age: 43, voted: false},
  {name: 'Joey', age: 41, voted: true},
  {name: 'Jeff', age: 30, voted: true},
  {name: 'Zack', age: 19, voted: false}
];
console.log(toNamedObj(voters)); 

about 4 years ago · Juan Pablo Isaza
2 answers
Answer question

0

Using your current approach, you'd have to be able to detect, when iterating, if the name already exists, if the object nested in it is a plain voter object (in which case it'd have to be removed and replaced with the array-like object), or if it's the array-like object. While that'd be possible, it'd be convoluted. A better approach would be to unconditionally group into arrays indexed by name, and then transform each array into either the single voter object, or into the {0: .. 1: } object afterwards.

const toNamedObj = (arr) => {
  const votersByName = {};
  for (const voter of voters) {
    votersByName[voter.name] ??= [];
    votersByName[voter.name].push(voter);
  }
  for (const [name, voters] of Object.entries(votersByName)) {
    votersByName[name] = voters.length === 1
      ? voters[0]
      : { ...voters };
  }
  return votersByName;
}

const voters = [
  {name:'Bob' , age: 30, voted: true},
  {name:'Bob' , age: 31, voted: true},
  {name:'Bob' , age: 32, voted: true},
  {name:'Jake' , age: 32, voted: true},
  {name:'Kate' , age: 25, voted: false},
  {name:'Jake' , age: 20, voted: false},
  {name:'Phil' , age: 21, voted: true},
  {name:'Ed' , age:55, voted:true},
  {name:'Tami' , age: 54, voted:true},
  {name: 'Mary', age: 31, voted: false},
  {name: 'Becky', age: 43, voted: false},
  {name: 'Joey', age: 41, voted: true},
  {name: 'Jeff', age: 30, voted: true},
  {name: 'Zack', age: 19, voted: false}
];
console.log(toNamedObj(voters));

Or by mapping the entries

const toNamedObj = (arr) => {
  const votersByName = {};
  for (const voter of voters) {
    votersByName[voter.name] ??= [];
    votersByName[voter.name].push(voter);
  }
  return Object.fromEntries(
    Object.entries(votersByName)
      .map(([name, voters]) => ([
        name,
        voters.length === 1 ? voters[0] : { ...voters }
      ]))
  );
}

const voters = [
  {name:'Bob' , age: 30, voted: true},
  {name:'Bob' , age: 31, voted: true},
  {name:'Bob' , age: 32, voted: true},
  {name:'Jake' , age: 32, voted: true},
  {name:'Kate' , age: 25, voted: false},
  {name:'Jake' , age: 20, voted: false},
  {name:'Phil' , age: 21, voted: true},
  {name:'Ed' , age:55, voted:true},
  {name:'Tami' , age: 54, voted:true},
  {name: 'Mary', age: 31, voted: false},
  {name: 'Becky', age: 43, voted: false},
  {name: 'Joey', age: 41, voted: true},
  {name: 'Jeff', age: 30, voted: true},
  {name: 'Zack', age: 19, voted: false}
];
console.log(toNamedObj(voters));

I recommend against using .reduce when the accumulator is the same value on each iteration - a plain for loop is simpler to write and understand.

about 4 years ago · Juan Pablo Isaza Report

0

Presented below is one possible way to achieve the desired objective.

Code Snippet

// method to transform arr to desired format
const toNamedObj = arr => (
  arr.reduce(
    (acc, {name, voted, ...rest}, idx) => (
      (acc[name] ??= []).push({ name, voted, ...rest }),
      (idx === arr.length - 1 && Object.entries(acc).forEach(
          ([k, v]) => (acc[k] = (v.length === 1) ? v[0] : {...v})
      )),
      acc
    ),
    {}
  )
);
/* with explanation
// method to transform arr to desired format
const toNamedObj = arr => (
  // iterate using ".reduce()"
  arr.reduce(
    (acc, {name, voted, ...rest}, idx) => {
      // if "name" not present in "acc", set it as empty array
      acc[name] ??= [];
      // push elt into array
      acc[name].push({ name, voted, ...rest });
      // when iterating over the last item, special processing
      if (idx === arr.length - 1) {    // processed last elt in arr
        // review each value in "acc" and convert to object
        Object.entries(acc)
        .forEach(([k, v]) => {
          if (v.length === 1) {       // only one elt in value-array
            // place the elt as-is
            acc[k] = v[0];
          } else {                    // multiple elts in val-arr
            // convert val-arr to object
            acc[k] = {...v};
          }
        });
      };
      // always return the accumulator "acc" in ".reduce()" callback
      return acc;
    },
    {}        // "acc" is set as an empty object initially
  )
);
*/

const voters = [
  {name:'Bob' , age: 30, voted: true},
  {name:'Bob' , age: 31, voted: true},
  {name:'Bob' , age: 32, voted: true},
  {name:'Jake' , age: 32, voted: true},
  {name:'Kate' , age: 25, voted: false},
  {name:'Jake' , age: 20, voted: false},
  {name:'Phil' , age: 21, voted: true},
  {name:'Ed' , age:55, voted:true},
  {name:'Tami' , age: 54, voted:true},
  {name: 'Mary', age: 31, voted: false},
  {name: 'Becky', age: 43, voted: false},
  {name: 'Joey', age: 41, voted: true},
  {name: 'Jeff', age: 30, voted: true},
  {name: 'Zack', age: 19, voted: false}
];
console.log(toNamedObj(voters));
.as-console-wrapper { max-height: 100% !important; top: 0 }

Explanation

Inline comments added to the snippet above.

about 4 years ago · Juan Pablo Isaza Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!