Группировать массив по нескольким полям - PullRequest
0 голосов
/ 13 мая 2018

У меня есть ответ 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, чтобы эта логика находилась на стороне сервера. А пока я бы хотел узнать, как это сделать.

Ответы [ 3 ]

0 голосов
/ 13 мая 2018

Исправление ответа к группе:

Сначала мы создаем массив с желаемой структурой объекта

    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)}]
0 голосов
/ 13 мая 2018

Я бы подошел к этому, уменьшив массив ([см. Array.prototype.reduce] [1]

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

По сути, создайте совокупный массив результатов, когда вы будете перебирать каждый (текущий) объект в данных "людей". Проверьте, существует ли объект с ключом человека. Если это так, добавьте нового питомца в соответствующий массив питомцев. Иначе, создайте нового человека с текущим домашним животным.

Обязательно инициализируйте функцию 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);
0 голосов
/ 13 мая 2018

Вы можете использовать reduce, чтобы обобщить ваши данные для объекта.Используйте Object.values для преобразования объекта в массив

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

let result = Object.values(people.reduce((c, {pet,...u}) => {
  let k = u.name + u.surname;             //Make a key using name and last name
  let {type,...p} = pet;                  //Deconstrct pet object
  type = type.toLowerCase();              //Make type lower case
  c[k] = c[k] || u;                       //Initiate if name does not exist
  c[k][type] = c[k][type] || [];          //Initiate if pet does not exist

  c[k][type].push(p);                     //Add the pet
  return c;
}, {}));

console.log(result);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...