โครงสร้างใหม่ของอาร์เรย์ JavaScript


16

ฉันมีอาร์เรย์พร้อมที่อยู่ของนักเรียนและผู้ปกครอง

ตัวอย่างเช่น,

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

ฉันพยายามจัดรูปแบบใหม่ตามผลลัพธ์ต่อไปนี้

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

จนถึงตอนนี้ฉันได้ลองวิธีต่อไปนี้แล้ว ฉันไม่แน่ใจว่าเป็นวิธีที่ถูกต้องหรือไม่

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

1
ดังนั้นโดยย่อ - เพื่อให้ง่ายขึ้นสำหรับผู้อ่านในอนาคต - คุณต้องการรวมparent_addressและrelationshipเข้ากับparentวัตถุและรวมเข้าด้วยกันเมื่อพบชื่อและที่อยู่อีเมลซ้ำกัน
ลูอิส

2
วิธีที่อยู่พาเรนต์สามารถทำได้อย่างไร คุณสมบัติใดที่ควรใช้เพื่อเชื่อมโยงพวกเขา ขอบคุณล่วงหน้า! :)
StepUp

ข้อมูลโค้ดในตอนท้ายไม่ตรงกับโครงสร้างข้อมูล คุณบอกว่าconst list = []ในตอนแรก user[0]แต่ที่ด้านล่างที่คุณย้ำกว่ารายการที่เห็นได้ชัดจากการทำซ้ำมากกว่า รหัสตัวอย่างของคุณควรสอดคล้องกัน
TKoL

@Lewis ใช่ฉันต้องการเหมือนกับที่คุณพูดถึง
kathy

@SteUp ค่าเหล่านั้นจะถูกเรียกคืนจากฐานข้อมูลที่มีอยู่ของฉันและเข้าร่วมกับตารางนักเรียนและผู้ปกครอง สิ่งที่ฉันมีเพียง ID นักเรียนในตารางผู้ปกครอง
kathy

คำตอบ:


12

วิธีหนึ่งที่จะใช้.reduce()กับวัตถุเป็นตัวสะสม สำหรับแต่ละ id, คุณสามารถจัดเก็บวัตถุที่เกี่ยวข้องกับอาร์เรย์พ่อแม่ซึ่งคุณสามารถผนวกกับในของคุณ.reduce()โทรกลับเมื่อใดก็ตามที่คุณพบวัตถุใหม่ที่มีรหัสเดียวกัน จากนั้นเมื่อต้องการรับอาร์เรย์ของวัตถุจากวัตถุของคุณคุณสามารถโทรหาObject.values()มันได้

ดูตัวอย่างด้านล่าง:

const users = [{ id: 1, name: 'John', email: 'johnson@mail.com', age: 25, parent_address: 'USA', relationship: 'mother' }, { id: 1, name: 'John', email: 'johnson@mail.com', age: 25, parent_address: 'Spain', relationship: 'father' }, { id: 2, name: 'Mark', email: 'mark@mail.com', 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);

เนื่องจากคุณพูดถึงคุณใหม่สำหรับ JS การทำความเข้าใจในวิธีที่จำเป็นยิ่งขึ้นนั้นง่ายกว่า (ดูความคิดเห็นเกี่ยวกับโค้ดสำหรับรายละเอียด):

const users = [{ id: 1, name: 'John', email: 'johnson@mail.com', age: 25, parent_address: 'USA', relationship: 'mother' }, { id: 1, name: 'John', email: 'johnson@mail.com', age: 25, parent_address: 'Spain', relationship: 'father' }, { id: 2, name: 'Mark', email: 'mark@mail.com', 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);


ขอบคุณฉันจะตรวจสอบรายละเอียดและฉันมีอยู่มากที่จะอ่านรหัสของคุณ
kathy

โอ้นี่เป็นของแข็ง destructuring วัตถุในreduceการเรียกกลับเป็นสิ่งที่ดี แต่อาจจะหนักหน่อยสำหรับการเริ่มต้นแม้ว่า
TKoL

1
@TKoL ขอบคุณผมจะพยายามและเพิ่ม "ง่าย" รุ่น
นิคพาร์สันส์

1
รุ่นที่เรียบง่ายดูดีมาก!
TKoL

1
ขอบคุณมาก. ฉันอ่านรหัสของคุณและเข้าใจง่ายโดยเฉพาะอย่างยิ่งในข้อมูลโค้ดที่สอง ขอบคุณคำตอบของสมาชิกคนอื่นเช่นกัน อีกครั้งขอบคุณพวกคุณมาก
kathy

5

คุณสามารถลดอาร์เรย์และค้นหาผู้ใช้ที่มี ID เดียวกันและเพิ่มข้อมูลหลักลงไป

หากไม่พบผู้ใช้ให้เพิ่มผู้ใช้ใหม่ในชุดผลลัพธ์

const
    users = [{ id: 1, name: 'John', email: 'johnson@mail.com', age: 25, parent_address: 'USA', relationship: 'mother' }, { id: 1, name: 'John', email: 'johnson@mail.com', age: 25, parent_address: 'Spain', relationship: 'father' }, { id: 2, name: 'Mark', email: 'mark@mail.com', 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; }


2

ข้อมูลการปรับโครงสร้างเช่นนี้ค่อนข้างธรรมดาและArray.reduce()ออกแบบมาสำหรับงาน มันเป็นวิธีที่แตกต่างในการดูสิ่งต่าง ๆ และทำความคุ้นเคยกับบางสิ่ง แต่หลังจากคุณเขียนโค้ดสองสามครั้งมันจะกลายเป็นลักษณะที่สอง

reduce() ถูกเรียกบนอาเรย์และรับพารามิเตอร์สองตัว:

  1. ฟังก์ชั่นที่จะถูกเรียกสำหรับแต่ละองค์ประกอบในอาร์เรย์
  2. ค่าเริ่มต้น

ฟังก์ชั่นของคุณจะถูกเรียกใช้สำหรับแต่ละองค์ประกอบที่มีค่าเริ่มต้นสำหรับการเรียกใช้ครั้งแรกหรือค่าส่งคืนจากการเรียกใช้ฟังก์ชันก่อนหน้าสำหรับการเรียกใช้ครั้งต่อไปตามองค์ประกอบอาร์เรย์ดัชนีในอาร์เรย์ดั้งเดิมและอาร์เรย์ต้นฉบับที่ลด () ถูกเรียกใช้ (สองรายการสุดท้ายมักจะถูกละเว้นและไม่จำเป็น) ควรส่งคืนวัตถุหรือสิ่งที่คุณสร้างขึ้นด้วยองค์ประกอบปัจจุบันที่เพิ่มเข้ามาและค่าส่งคืนนั้นจะถูกส่งผ่านไปยังการเรียกใช้ฟังก์ชันถัดไปของคุณ

สำหรับสิ่งต่าง ๆ เช่นนี้ฉันมักจะมีวัตถุเก็บคีย์ที่ไม่ซ้ำกัน ( idสำหรับคุณ) แต่ฉันเห็นว่าคุณต้องการคืนอาร์เรย์ นั่นคือหนึ่งบรรทัดในการแมปวัตถุและคีย์ไปยังอาร์เรย์และมีประสิทธิภาพมากขึ้นในการใช้กลไกคุณสมบัติวัตถุในตัวแทนที่จะเป็น array.find () เพื่อดูว่าคุณได้เพิ่มรหัสแล้ว

const users = [{
    id: 1,
    name: 'John',
    email: 'johnson@mail.com',
    age: 25,
    parent_address: 'USA',
    relationship:'mother'
  },
  {
    id: 1,
    name: 'John',
    email: 'johnson@mail.com',
    age: 25,
    parent_address: 'Spain',
    relationship:'father'
  },
  {
    id: 2,
    name: 'Mark',
    email: 'mark@mail.com',
    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);


1

คุณต้องวนซ้ำสองครั้งโดยใช้วิธีการปัจจุบัน ความซับซ้อนคือ O (n ^ 2) (สำหรับ Loop + indexOf)

วิธีที่ดีกว่าคือการทำดัชนีอาร์เรย์และใช้คีย์อาร์เรย์สำหรับการตรวจจับการทำซ้ำและการค้นหา

ตัวอย่างเช่น:

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

ขอบคุณฉันจะตรวจสอบรายละเอียดและฉันมีอยู่มากที่จะอ่านรหัสของคุณ
kathy

1
const users = [{
    id: 1,
    name: 'John',
    email: 'johnson@mail.com',
    age: 25,
    parent_address: 'USA',
    relationship:'mother'
  },
  {
    id: 1,
    name: 'John',
    email: 'johnson@mail.com',
    age: 25,
    parent_address: 'Spain',
    relationship:'father'
  },
  {
    id: 2,
    name: 'Mark',
    email: 'mark@mail.com',
    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)

1
คำอธิบายจะอยู่ในลำดับ เช่นคุณเปลี่ยนอะไร และทำไม?
ปีเตอร์มอร์เทนเซ่น

1

คุณสามารถใช้Mapคอลเล็กชันเพื่อจัดเก็บรายการที่ไม่ซ้ำกันและเพียงเติมโดยใช้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: 'johnson@mail.com',
    age: 25,
    parent_address: 'USA',
    relationship: 'mother'
  },
  {
    id: 1,
    name: 'John',
    email: 'johnson@mail.com',
    age: 25,
    parent_address: 'Spain',
    relationship: 'father'
  },
  {
    id: 2,
    name: 'Mark',
    email: 'mark@mail.com',
    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 })));


0

 const users = [{
    id: 1,
    name: 'John',
    email: 'johnson@mail.com',
    age: 25,
    parent_address: 'USA',
    relationship:'mother'
  },
  {
    id: 1,
    name: 'John',
    email: 'johnson@mail.com',
    age: 25,
    parent_address: 'Spain',
    relationship:'father'
  },
  {
    id: 2,
    name: 'Mark',
    email: 'mark@mail.com',
    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));
});

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.