Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

311
Vistas
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 Respuestas
Responde la pregunta

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 Denunciar

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 Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda