Как преобразовать одну структуру данных в другую

Есть следующая структура данных

const persons = [
        {
          id: 1,
          fio: 'Иванов И.И',
          workPlaces: [
            {
              id: 222,
              name: 'Рога и копыта',
            },
            {
              id: 12,
              name: 'крутая контора',
            },
          ],
        },
        {
          id: 2,
          fio: 'Сидоров Е.Е',
          workPlaces: [
            {
              id: 222,
              name: 'Рога и копыта',
            },
            {
              id: 333,
              name: 'Другое место',
            },
          ],
        },
        {
          id: 3,
          fio: 'Козлов А.А',
          workPlaces: [
            {
              id: 333,
              name: 'Другое место',
            },
          ],
        },
      ]

Как мне из этих данны получить следующий набор данных:

const workPlaces = [
        {
          id: 222,
          name: 'Рога и копыта',
          members: [
            {
              id: 1,
              fio: 'Иванов И.И',
            },
            {
              id: 2,
              fio: 'Сидоров Е.Е',
            },
          ],
        },
        {
          id: 333,
          name: 'Другое место',
          members: [
            {
               id: 2,
               fio: 'Сидоров Е.Е',
            },
            {
               id: 3,
               fio: 'Козлов А.А',
            },
          ],
        },
        {
          id: 12,
          name: 'крутая контора',
          members: [
            {
              id: 1,
              fio: 'Иванов И.И',
            },
          ],
        },
      ]

Я пытался что то такое изобразить

      const userData = []

      persons.forEach((person) => {
        person.workPlaces.forEach((place) => {
          const membersArr = []

          if (person.workPlaces.includes(place)) {
              membersArr.push(person)
          }

          userData.push({
            clinicId: place.id,
            doctors: membersArr,
          })
        })
      })

Но так получается плоская структура, потому что я массив пересоздаю каждый раз, но я просто не могу додуматься, как мне вложить в него данные корректно, если у врача такая клиника уже есть


Ответы (2 шт):

Автор решения: UserTest013

function getWorkPlaces(persons) {
  return persons.reduce((workPlaces, person) => {
    person.workPlaces.forEach((workPlace) => {
      let place = workPlaces.find((wp) => workPlace.id === wp.id);
      if (!place) {
        place = { ...workPlace};
        place.members = [];
        workPlaces.push(place);
      }
      const per = { ...person};
      delete per.workPlaces;
      place.members.push(per);
    });
    return workPlaces;
  }, []);
}

const persons = [{
    id: 1,
    fio: 'Иванов И.И',
    workPlaces: [{
        id: 222,
        name: 'Рога и копыта',
      },
      {
        id: 12,
        name: 'крутая контора',
      },
    ],
  },
  {
    id: 2,
    fio: 'Сидоров Е.Е',
    workPlaces: [{
        id: 222,
        name: 'Рога и копыта',
      },
      {
        id: 333,
        name: 'Другое место',
      },
    ],
  },
  {
    id: 3,
    fio: 'Козлов А.А',
    workPlaces: [{
      id: 333,
      name: 'Другое место',
    }, ],
  },
];

console.log(getWorkPlaces(persons));

→ Ссылка
Автор решения: EzioMercer

Мой алгоритм основан на том, что у двух рабочих мест не может быть одинакового id, а если это не так то всегда вместо простого id сможете использовать уникальный ключ для рабочего места. И не надо постоянно бегать по массиву и что-то искать - это очень долго будет работать на больших размерах. Всегда старайтесь придумывать алгоритмы, где не надо бегать по массиву больше одного раза. В данном случае достаточно использовать new Map() в котором элемент ищется со сложностью O(1). Вы точно так же можете вместо new Map() использовать обычный объект, при желании. Вот вам простой, понятный и быстрый алгоритм:

const persons = [{
    id: 1,
    fio: 'Иванов И.И',
    workPlaces: [{
        id: 222,
        name: 'Рога и копыта',
      },
      {
        id: 12,
        name: 'крутая контора',
      },
    ],
  },
  {
    id: 2,
    fio: 'Сидоров Е.Е',
    workPlaces: [{
        id: 222,
        name: 'Рога и копыта',
      },
      {
        id: 333,
        name: 'Другое место',
      },
    ],
  },
  {
    id: 3,
    fio: 'Козлов А.А',
    workPlaces: [{
      id: 333,
      name: 'Другое место',
    }],
  },
]

const getPersonsWorkPlaces = (persons) => {
  const workPlacesMap = new Map();

  persons.forEach(person => {
    const personData = {
      id: person.id,
      fio: person.fio
    };

    person.workPlaces.forEach(workPlace => {
      if (workPlacesMap.has(workPlace.id)) {
        workPlacesMap.get(workPlace.id).members.push(personData);
        return;
      }

      workPlacesMap.set(workPlace.id, {...workPlace, members: [personData]});
    })
  })

  return [...workPlacesMap.values()];
}

const workPlaces = getPersonsWorkPlaces(persons);

console.log(workPlaces);

→ Ссылка