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_addressrelationship放入一个parent对象,并在找到重复的名称和电子邮件地址时合并它们。
刘易斯

2
如何取得家长地址?应该使用什么属性来关联它们?在此先感谢!:)
StepUp

最后的代码段与数据结构不匹配。您const list = []首先说,但是在底部,您显然通过遍历遍历了该列表user[0]。您的示例代码应保持一致。
TKoL

@刘易斯,是的,我想和你提到的一样。
凯西

@SteUp,这些值从我现有的数据库中检索出来,并与student和parent表一起加入。我只有父母表中的学生证。
凯西

Answers:


12

一种方法是将.reduce()对象用作累加器。对于每个ID,您可以存储一个带有parents数组的关联对象,当遇到具有相同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);


谢谢,我将检查详细信息,并且我非常有资格阅读您的代码。
凯西

哦,这很可靠。reduce回调中的对象分解很不错,但是对于初学者来说可能有点沉重。
TKoL

1
@TKoL谢谢,我将尝试添加一个“简单”版本
Nick Parsons

1
简单的版本看起来很棒!
TKoL

1
非常感谢。我阅读了您的代码,并且易于理解,尤其是在第二代码段中。也赞赏其他成员的回答。同样,非常感谢你们。
凯西

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. 起始值

然后,对每个元素调用函数,并使用第一个运行的起始值或每个后续运行的前一个函数调用的返回值,沿着数组元素,索引到原始数组,以及原始数组reduce()被调用(通常忽略后两个,并且几乎不需要)。它应该返回对象或添加了当前元素的任何东西,并且该返回值将传递给函数的下一个调用。

对于这样的事情,我通常有一个对象(id为您保留唯一键),但是我看到您想要返回一个数组。这是将对象和键映射到数组的一行,并且使用内置对象属性机制而不是array.find()来查看是否已添加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'
  }
];

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

谢谢,我将检查详细信息,并且我非常有资格阅读您的代码。
凯西

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

您可以使用Mapcollection来存储唯一项,并使用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));
});

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.