按多个字段对数组进行分组

时间:2018-05-13 16:25:57

标签: javascript angular

我有一个api响应,看起来像这样:

people = [
    {
      name: 'John',
      surname: 'Doe',
      pet: {
          type: 'CAT',
          name: 'whiskers',
          age: 1
      }
    },
    {
      name: 'John',
      surname: 'Doe',
      pet: {
        type: 'DOG',
        name: 'Dexter',
        age: 4
      }
    },
    {
      name: 'Jane',
      surname: 'Doe',
      pet: {
        type: 'CAT',
        name: 'Fluffy',
        age: 10
      }
    },
    {
      name: 'Jane',
      surname: 'Doe',
      pet: {
        type: 'CAT',
        name: 'Dennis',
        age: 3
      }
    }
  ]

我想翻译它,看起来像这样(只有两种类型的宠物):

people = [
    {
        “name”: “John”,
        “surname”: “Doe”,
        “cats”: [
            {
                “name”: “whiskers”,
                “age”: 1
            }
        ],
        “dogs”: [
            {
                “name”: “Dexter”,
                “age”: 4
            }
        ]
    },
    {
        “name”: “Jane”,
        “surname”: “Doe”,
        “cats”: [
            {
                “name”: “Fluffy”,
                “age”: 10
            },
            {
                “name”: “Dennis”,
                “age”: 3
            }
        ]
    }
]

我正在使用角度5.我需要能够显示类似于:

的表格
<table>
  <thead>
  <tr>
    <th>Name</th>
    <th>Surname</th>
    <th>Cats</th>
    <th>Dogs</th>
  </tr>
  </thead>
  <tbody>
  <tr *ngFor="let person of people">
    <td>{{person.name}}</td>
    <td>{{person.surname}}</td>
    <td>
      <ul>
        <li *ngFor="let cat of person.cats">name: {{cat.name}}, age: {{cat.age}} years</li>
      </ul>
    </td>
    <td>
      <ul>
        <li *ngFor="let dog of person.dogs">name: {{dog.name}}, age: {{dog.age}} years</li>
      </ul>
    </td>
  </tr>
  </tbody>
</table>

我试着在我去的时候循环构建一张地图但是我努力将它从地图转换回到最后的数组。我也希望有一个我错过的更清洁的解决方案:

const peopleMap = new Map;
this.people.forEach(person => {
  const key = person.name + '_' + person.surname;
  if (peopleMap[key]) {
    if (person.pet.type === 'CAT') {
      peopleMap[key].cats.push(new Pet(person.pet.name, person.pet.age));
    } else {
      peopleMap[key].dogs.push(new Pet(person.pet.name, person.pet.age));
    }
  } else {
    let cats: [Pet];
    let dogs: [Pet];
    if (person.pet.type === 'CAT') {
      cats.push(new Pet(person.pet.name, person.pet.age));
    } else {
      dogs.push(new Pet(person.pet.name, person.pet.age));
    }
    peopleMap[key] = new Person(person.name, person.surname, cats, dogs);
  }
});

理想情况下,我将被允许更改api,以便此逻辑位于服务器端。与此同时,我很想知道如何做到这一点。

2 个答案:

答案 0 :(得分:0)

  

修复群组的答案:

首先,我们创建具有所需对象结构的数组

    var newPeople = people.map(function(item){
    var newPeople = {};
    newPeople.name = item.name;
    newPeople.surname = item.surname;
    var type = item.pet.type; 
    newPeople[type] = [];
    var petDetails = {
        name: item.pet.name,
        age: item.pet.age
    } 
    newPeople[type].push(petDetails);
    return newPeople;
});

/*Output: people = [{name: "John", surname: "Doe", CAT: Array(1)},
                    {name: "John", surname: "Doe", DOG: Array(1)}, 
                    {name: "Jane", surname: "Doe", CAT: Array(1)},
                    {name: "Jane", surname: "Doe", CAT: Array(1)}]*/

现在我们将使用以下循环对它们进行分组:

for (var i = 0; i < newPeople.length; i++) {
    for(var j = i+1; j < newPeople.length;j++){
        var item = newPeople[i];
        var nextItem = newPeople[j];
        if(item.name === nextItem.name && item.surname === nextItem.surname) {
            var firstItemKeys = Object.keys(item);
            var nextItemKeys = Object.keys(nextItem);
            if(firstItemKeys[2] === nextItemKeys[2]) {
                item[firstItemKeys[2]].push(nextItem[nextItemKeys[2]][0]);
            } else {
                if (Array.isArray(item[nextItemKeys[2]])) {
                    item[nextItemKeys[2]].push(nextItem[nextItemKeys[2]][0]);
                } else {
                    item[nextItemKeys[2]] = [];
                    item[nextItemKeys[2]].push(nextItem[nextItemKeys[2]][0]);
                }
            }
            newPeople.splice(j,1);
            j--
        }   
    }       
}

输出符合预期:

newPeople = [{name: "John", surname: "Doe", CAT: Array(1), DOG: Array(1)},
{name: "Jane", surname: "Doe", CAT: Array(2)}]

答案 1 :(得分:0)

我会通过减少数组来解决这个问题([参见Array.prototype.reduce] [1]

[1]:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce

基本上,当你遍历&#34; people&#34;中的每个(当前)对象时,创建一个聚合结果数组。数据。检查是否存在具有person键的对象。如果他们这样做,将新宠物添加到适当的宠物阵列。否则,用当前的宠物创造一个新人。

确保使用空数组初始化reduce函数(调用groupByPerson时的第二个参数)。

let people = [{name: 'John',surname: 'Doe',pet: {type: 'CAT',name: 'whiskers',age: 1}},{name: 'John',surname: 'Doe',pet: {type: 'DOG', name: 'Dexter',age: 4}},{name: 'Jane',surname: 'Doe',pet: {type: 'CAT',name: 'Fluffy',age: 10}},{name: 'Jane',surname: 'Doe',pet: {type: 'CAT',name: 'Dennis',age: 3}}];
function groupByPerson(data) {
        return data.reduce( function (results, current) {
            const key = current.name + "_" + current.surname;
            let matched = results.filter(r => r.key === key)[0];
            if (matched) {
                let petArray = (current.pet.type === "CAT") ? matched.cats : matched.dogs;
                petArray.push({name:current.pet.name, age:current.pet.age});
            }
            else { 
                let newPerson = {
                    key: key,
                    name: current.name,
                    surname: current.surname,
                    cats: [],
                    dogs: []
                }
                let petArray = (current.pet.type === "CAT") ? newPerson.cats : newPerson.dogs;
                petArray.push({name:current.pet.name, age:current.pet.age});
                results.push(newPerson); 
            }    
            return results;
        }, []); // initialize with empty array
    }
    const results = groupByPerson(people);