JavaScript-Array neu strukturieren

16

Ich habe ein Array mit Schüler- und Elternadressen.

Zum Beispiel,

  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'
  }
];

Ich versuche dies auf das folgende Ergebnis umzuformatieren.

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'
        }
    ]
}
];

Bisher habe ich Folgendes versucht. Ich bin mir nicht sicher, ob das der richtige Weg ist oder nicht.

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
quelle
1
Kurz gesagt, um es zukünftigen Lesern zu erleichtern, möchten Sie ein Objekt kombinieren parent_addressund relationshipzu einem parentObjekt zusammenfügen, wenn ein doppelter Name und eine doppelte E-Mail-Adresse gefunden werden.
Lewis
2
Wie kann die Elternadresse genommen werden? Welche Eigenschaft sollte verwendet werden, um sie in Beziehung zu setzen? Vielen Dank im Voraus! :)
StepUp
Das Code-Snippet am Ende stimmt nicht mit der Datenstruktur überein. Sie sagen const list = []zuerst, aber unten iterieren Sie diese Liste anscheinend, indem Sie sie wiederholen user[0]. Ihr Beispielcode sollte konsistent sein.
TKoL
@ Lewis ja, ich will genau wie du erwähnt hast.
Kathy
@SteUp, diese Werte werden aus meiner vorhandenen Datenbank abgerufen und mit der Schüler- und Elterntabelle verknüpft. Was ich habe nur Studentenausweis in der Elterntabelle.
Kathy

Antworten:

12

Ein Ansatz wäre die Verwendung .reduce()eines Objekts als Akkumulator. Für jede ID können Sie ein zugeordnetes Objekt mit einem übergeordneten Array speichern, an das Sie in Ihrem .reduce()Rückruf anhängen können, wenn Sie auf ein neues Objekt mit derselben ID stoßen. Dann einen Array von Objekten aus dem Objekt zu erhalten, können Sie rufen Object.values()auf sie

Siehe Beispiel unten:

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);

Da Sie erwähnt haben, dass Sie neu bei JS sind, ist es möglicherweise einfacher, dies zwingender zu verstehen (Einzelheiten finden Sie in den Codekommentaren):

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
quelle
Danke, ich werde die Details überprüfen und bin sehr existent, um Ihren Code zu lesen.
Kathy
Oh, das ist solide. Die Objektzerstörung im reduceRückruf ist nett, aber für Anfänger vielleicht etwas schwer.
TKoL
1
@TKoL danke, ich werde versuchen, eine "einfachere" Version hinzuzufügen
Nick Parsons
1
Die einfachere Version sieht gut aus!
TKoL
1
Ich danke dir sehr. Ich habe Ihren Code gelesen und bin besonders beim zweiten Code-Snippet leicht zu verstehen. Schätzen Sie auch die Antwort anderer Mitglieder. Nochmals vielen Dank Jungs.
Kathy
5

Sie können das Array verkleinern und nach einem Benutzer mit derselben ID suchen und die übergeordneten Informationen hinzufügen.

Wenn der Benutzer nicht gefunden wird, fügen Sie der Ergebnismenge einen neuen Benutzer hinzu.

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
quelle
2

Solche Umstrukturierungsdaten sind weit verbreitet und Array.reduce()für diese Aufgabe konzipiert. Es ist eine andere Art, Dinge zu betrachten und gewöhnungsbedürftig, aber nachdem Sie den Code ein paar Mal geschrieben haben, wird es zur zweiten Natur.

reduce() wird auf einem Array aufgerufen und akzeptiert zwei Parameter:

  1. Eine Funktion, die für jedes Element im Array aufgerufen wird
  2. der Startwert

Ihre Funktion wird dann für jedes Element mit dem Startwert für den ersten Lauf oder dem Rückgabewert aus dem vorherigen Funktionsaufruf für jeden nachfolgenden Lauf entlang des Array-Elements aufgerufen, in das ursprüngliche Array indiziert und das ursprüngliche Array, das reduziert () wurde aufgerufen (die letzten beiden werden normalerweise ignoriert und selten benötigt). Es sollte das Objekt oder was auch immer Sie mit dem aktuell hinzugefügten Element aufbauen, zurückgeben, und dieser Rückgabewert wird an den nächsten Aufruf Ihrer Funktion übergeben.

Für solche Dinge habe ich normalerweise ein Objekt, um die eindeutigen Schlüssel ( idfür Sie) zu behalten , aber ich sehe, dass Sie ein Array zurückgeben möchten. Das ist eine Zeile, um das Objekt und die Schlüssel einem Array zuzuordnen, und es ist effizienter, den integrierten Objekteigenschaftsmechanismus anstelle von array.find () zu verwenden, um festzustellen, ob Sie bereits eine ID hinzugefügt haben.

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
quelle
1

Sie müssen mit der aktuellen Methode zweimal iterieren. Die Komplexität ist O (n ^ 2). (für Loop + indexOf)

Eine bessere Möglichkeit besteht darin, das Array zu indizieren und den Array-Schlüssel zur Erkennung und Suche von Duplikaten zu verwenden.

Zum Beispiel:

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
quelle
Danke, ich werde die Details überprüfen und bin sehr existent, um Ihren Code zu lesen.
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.
quelle
1
Eine Erklärung wäre angebracht. ZB was hast du geändert? Und warum?
Peter Mortensen
1

Sie können die MapSammlung verwenden, um eindeutige Elemente zu speichern und sie einfach mit filterfolgenden Elementen zu füllen :

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
quelle
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
quelle