Tableau JavaScript re structure

16

J'ai un tableau avec les adresses des étudiants et des parents.

Par exemple,

  const users = [{
    id: 1,
    name: 'John',
    email: '[email protected]',
    age: 25,
    parent_address: 'USA',
    relationship:'mother'
  },
  {
    id: 1,
    name: 'John',
    email: '[email protected]',
    age: 25,
    parent_address: 'Spain',
    relationship:'father'
  },
  {
    id: 2,
    name: 'Mark',
    email: '[email protected]',
    age: 28,
    parent_address: 'France',
    relationship:'father'
  }
];

J'essaye de reformater ceci au résultat suivant.

const list = [
{
    id: 1,
    name: 'John',
    email: '[email protected]',
    age: 25,
    parent: [
        {
            parent_address: 'USA',
            relationship:'mother'
        },{
            parent_address: 'Spain',
            relationship:'father'
        }
    ]
},
{
    id: 2,
    name: 'Mark',
    email: '[email protected]',
    age: 28,
    parent:[
        {
            parent_address: 'France',
            relationship:'father'
        }
    ]
}
];

Jusqu'à présent, j'ai essayé la manière suivante. Je ne suis pas sûr que ce soit la bonne façon ou non.

const duplicateInfo = [];
for (var i = 0; i < user[0].length; i++) {
    var parent = [];
    if (duplicateInfo.indexOf(user[0][i].id) != -1) {
        // Do duplicate stuff
    } else {
        // Do other
    }
    duplicateInfo.push(user[0][i].id);
}
kathy
la source
1
Donc, en bref - pour le rendre plus facile pour les futurs lecteurs - vous souhaitez combiner parent_addresset relationshipdans un parentobjet, et les fusionner lorsqu'un nom et une adresse e-mail en double sont trouvés.
Lewis
2
Comment l'adresse parentale peut-elle être prise? Quelle propriété utiliser pour les relier? Merci d'avance! :)
StepUp
L'extrait de code à la fin ne correspond pas à la structure des données. Vous dites const list = []au début, mais en bas vous parcourez cette liste apparemment en répétant user[0]. Votre exemple de code doit être cohérent.
TKoL
@Lewis oui, je veux exactement comme vous l'avez mentionné.
kathy
@SteUp, ces valeurs sont extraites de ma base de données existante et jointes à la table des élèves et des parents. Ce que j'ai seulement l'ID de l'élève dans la table des parents.
kathy

Réponses:

12

Une approche consisterait à utiliser .reduce()un objet comme accumulateur. Pour chaque identifiant, vous pouvez stocker un objet associé avec un tableau de parents auquel vous pouvez ajouter dans votre .reduce()rappel chaque fois que vous rencontrez un nouvel objet avec le même identifiant. Ensuite, pour obtenir un tableau d'objets de votre objet, vous pouvez appelerObject.values() sur elle

Voir l'exemple ci-dessous:

const users = [{ id: 1, name: 'John', email: '[email protected]', age: 25, parent_address: 'USA', relationship: 'mother' }, { id: 1, name: 'John', email: '[email protected]', age: 25, parent_address: 'Spain', relationship: 'father' }, { id: 2, name: 'Mark', email: '[email protected]', age: 28, parent_address: 'France', relationship: 'father' } ];
const res = Object.values(users.reduce((acc, {parent_address, relationship, ...r}) => { // use destructuring assignment to pull out necessary values
  acc[r.id] = acc[r.id] || {...r, parents: []}
  acc[r.id].parents.push({parent_address, relationship}); // short-hand property names allows us to use the variable names as keys
  return acc;
}, {}));

console.log(res);

Puisque vous avez mentionné que vous êtes nouveau dans JS, il peut être plus facile à comprendre de manière plus impérative (voir les commentaires de code pour plus de détails):

const users = [{ id: 1, name: 'John', email: '[email protected]', age: 25, parent_address: 'USA', relationship: 'mother' }, { id: 1, name: 'John', email: '[email protected]', age: 25, parent_address: 'Spain', relationship: 'father' }, { id: 2, name: 'Mark', email: '[email protected]', age: 28, parent_address: 'France', relationship: 'father' } ];

const unique_map = {}; // create an object - store each id as a key, and an object with a parents array as its value
for(let i = 0; i < users.length; i++) { // loop your array object
  const user = users[i]; // get the current object
  const id = user.id; // get the current object/users's id
  
  if(!(id in unique_map)) // check if current user's id is in the the object
    unique_map[id] = { // add the id to the unique_map with an object as its associated value 
      id: id,
      name: user.name,
      email: user.email,
      age: user.age,
      parents: [] // add `parents` array to append to later
    }
    
  unique_map[id].parents.push({ // push the parent into the object's parents array
    parent_address: user.parent_address,
    relationship: user.relationship
  });
}

const result = Object.values(unique_map); // get all values in the unique_map
console.log(result);

Nick Parsons
la source
Merci, je vais vérifier les détails et je suis très existé pour lire votre code.
kathy
Ooh c'est solide. L'objet déstructuré dans le reducerappel est agréable, mais peut-être un peu lourd pour un débutant.
TKoL
1
@TKoL merci, je vais essayer d'ajouter une version "plus simple"
Nick Parsons
1
La version plus simple a fière allure!
TKoL
1
Merci beaucoup. J'ai lu votre code et facile à comprendre, en particulier sur le deuxième extrait de code. J'apprécie également la réponse des autres membres. Encore une fois, merci beaucoup les gars.
kathy
5

Vous pouvez réduire le tableau et rechercher un utilisateur avec le même identifiant et y ajouter les informations parent.

Si l'utilisateur n'est pas trouvé, ajoutez un nouvel utilisateur à l'ensemble de résultats.

const
    users = [{ id: 1, name: 'John', email: '[email protected]', age: 25, parent_address: 'USA', relationship: 'mother' }, { id: 1, name: 'John', email: '[email protected]', age: 25, parent_address: 'Spain', relationship: 'father' }, { id: 2, name: 'Mark', email: '[email protected]', age: 28, parent_address: 'France', relationship: 'father' }],
    grouped = users.reduce((r, { parent_address, relationship, ...user }) => {
        var temp = r.find(q => q.id === user.id );
        if (!temp) r.push(temp = { ...user, parent: []});
        temp.parent.push({ parent_address, relationship });
        return r;
    }, []);

console.log(grouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Nina Scholz
la source
2

La restructuration de données comme celle-ci est assez courante et Array.reduce()est conçue pour la tâche. C'est une façon différente de voir les choses et il faut un certain temps pour s'y habituer, mais après avoir écrit le code plusieurs fois, cela devient une seconde nature.

reduce() est appelé sur un tableau et prend deux paramètres:

  1. une fonction qui sera appelée pour chaque élément du tableau
  2. la valeur de départ

Votre fonction est ensuite appelée pour chaque élément avec la valeur de départ pour la première exécution ou la valeur de retour de l'appel de fonction précédent pour chaque exécution suivante, le long de l'élément de tableau, indexez dans le tableau d'origine et le tableau d'origine qui réduit () a été appelé (les deux derniers sont généralement ignorés et rarement nécessaires). Il doit renvoyer l'objet ou tout ce que vous construisez avec l'élément actuel ajouté, et cette valeur de retour est transmise au prochain appel à votre fonction.

Pour des choses comme celle-ci, j'ai généralement un objet pour conserver les clés uniques ( idpour vous), mais je vois que vous voulez qu'un tableau soit retourné. C'est une ligne pour mapper l'objet et les clés à un tableau et il est plus efficace d'utiliser le mécanisme de propriété d'objet intégré au lieu de array.find () pour voir si vous avez déjà ajouté un identifiant.

const users = [{
    id: 1,
    name: 'John',
    email: '[email protected]',
    age: 25,
    parent_address: 'USA',
    relationship:'mother'
  },
  {
    id: 1,
    name: 'John',
    email: '[email protected]',
    age: 25,
    parent_address: 'Spain',
    relationship:'father'
  },
  {
    id: 2,
    name: 'Mark',
    email: '[email protected]',
    age: 28,
    parent_address: 'France',
    relationship:'father'
  }
];

let combined = users.reduce(
  // function called for each element in the array
  (previous, element) => {
    // previous starts out as the empty object we pass as the second argument
    // and will be the return value from this function for every other element
    
    // create an object for the id on our 'previous' object if it doesn't exist,
    // if it does exist we will trust the name, email, and age from the first
    // instance
    previous[element.id] = previous[element.id] || {
      id: element.id,
      name: element.name,
      age: element.age,
      parents: []
    };
    
    // now add parent
    previous[element.id].parents.push({
      parent_address: element.parent_address,
      relationship: element.relationship
    });
    
    // return our updated object, which will be passed to the next call
    // and eventually returned
    return previous;
  },
  {} // initial value is an empty object, no ids yet
);

// transform object into array with elements in order by key
let list = Object.keys(combined).sort().map(key => combined[key]);

console.dir(list);

Jason Goemaat
la source
1

Vous devez répéter deux fois en utilisant la méthode actuelle. La complexité est O (n ^ 2). (pour Loop + indexOf)

Une meilleure façon est d'indexer le tableau et d'utiliser la clé du tableau pour la détection et la recherche de duplication.

Par exemple:

const map = {};
users.forEach(user => {
    // Will return undefined if not exist
    let existing = map[user.id];
    if (!existing) {
        // If not exist, create new
        existing = {
            id: user.id,
            ...
            parents: [ {parent_address: user.parent_address, relationship: user.relationship ]
        }
    } else {
        // Otherwise, update only parents field
        // You can add other logic here, for example update fields if duplication is detected.
        existing.parents.push({parent_address: user.parent_address, relationship: user.relationship ]
        });
    }
    map[user.id] = existing;
})
// Convert the object to array
const list = map.values();
Anthony Poon
la source
Merci, je vais vérifier les détails et je suis très existé pour lire votre code.
kathy
1
const users = [{
    id: 1,
    name: 'John',
    email: '[email protected]',
    age: 25,
    parent_address: 'USA',
    relationship:'mother'
  },
  {
    id: 1,
    name: 'John',
    email: '[email protected]',
    age: 25,
    parent_address: 'Spain',
    relationship:'father'
  },
  {
    id: 2,
    name: 'Mark',
    email: '[email protected]',
    age: 28,
    parent_address: 'France',
    relationship:'father'
  }
];
const updatedUsers = users.map(user => {
    return {
    id: user.id,
    name: user.name,
    email: user.email,
    age: user.age,
    parent: [{
        relationship: user.relationship,
        parent_address: user.parent_address,
    }]
}
})

const list = updatedUsers.reduce((acc, user) => {
    const findIndex = acc.findIndex(eachUser => eachUser.id === user.id && eachUser.email === user.email);
    if (findIndex < 0) {
        acc.push(user);
        return acc;
    } else {
    acc[findIndex].parent.push(user.parent);
    return acc; 
    }
}, []);
console.log(list)
Maharjun M
la source
1
Une explication serait de mise. Par exemple, qu'avez-vous changé? Et pourquoi?
Peter Mortensen
1

Vous pouvez utiliser la Mapcollection pour stocker des éléments uniques et simplement la remplir en utilisant filter:

const unique = new Map(users.map(u=> 
    [u.id, {...u, parent: [...users.filter(f => f.id == u.id)]}]));

console.log(Array.from(unique, ([k, v])=> v)
    .map(s => ( { id: s.id, name: s.name, email: s.email, age:s.age, parent:s.parent })));

const users = [
  {
    id: 1,
    name: 'John',
    email: '[email protected]',
    age: 25,
    parent_address: 'USA',
    relationship: 'mother'
  },
  {
    id: 1,
    name: 'John',
    email: '[email protected]',
    age: 25,
    parent_address: 'Spain',
    relationship: 'father'
  },
  {
    id: 2,
    name: 'Mark',
    email: '[email protected]',
    age: 28,
    parent_address: 'France',
    relationship: 'father'
  }
];

const unique = new Map(users.map(u=> 
    [u.id, {...u, parent: [...users.filter(f => f.id == u.id)]}]));

console.log(Array.from(unique, ([k, v])=> v).map(s => ( 
    { id: s.id, name: s.name, email: s.email, age:s.age, parent:s.parent })));

StepUp
la source
0

 const users = [{
    id: 1,
    name: 'John',
    email: '[email protected]',
    age: 25,
    parent_address: 'USA',
    relationship:'mother'
  },
  {
    id: 1,
    name: 'John',
    email: '[email protected]',
    age: 25,
    parent_address: 'Spain',
    relationship:'father'
  },
  {
    id: 2,
    name: 'Mark',
    email: '[email protected]',
    age: 28,
    parent_address: 'France',
    relationship:'father'
  }
];
ids = new Map()
for (const user of users) {
  var newuser;
  if (ids.has(user.id)) {
    newuser = ids.get(user.id);
  } else {
    newuser = {};
    newuser.id = user.id;
    newuser.name = user.name;
    newuser.email = user.email;
    newuser.age = user.age;
    newuser.parent = [];
  }
  relationship = {};
  relationship.parent_address = user.parent_address;
  relationship.relationship = user.relationship;
  newuser.parent.push(relationship)
  ids.set(user.id, newuser);
}
list = [ ...ids.values() ];
list.forEach((u) => {
  console.log(JSON.stringify(u));
});

JGFMK
la source