Сбор массива в сегменты подмассива на основе значения (которое само по себе является массивом)

#javascript #ecmascript-6

#javascript #ecmascript-6

Вопрос:

У меня есть массив объектов Javascript, как показано ниже.

 [
  {
    email: 'alex@test.com',
    fn: 'Alex',
    sn: 'McPherson',
    phone: '01233xxxxx',
    hours: '40',
    rate: '20',
    amount: '200',
    vat: '60',
    agency: 'test',
    start: '08/06/2017',
    end: '10/06/2017',
    shipping: {
      addresses: [
        {
          id: '1234',
          area: 'xzy'
        },
        {
          id: '2345',
          area: 'uhj'
        }
      ]
    }
  },
  {
    email: 'mike@test.com',
    fn: 'Mike',
    sn: 'Mann',
    phone: '01233xxxxx',
    hours: '50',
    rate: '70',
    amount: '500',
    vat: '90',
    agency: 'test',
    start: '08/06/2017',
    end: '10/06/2017',
    shipping: {
      addresses: [
        {
          id: '1234',
          area: 'xzy'
        },
        {
          id: '3456',
          area: 'uio'
        }
      ]
    }
  },
  {
    email: 'fred@test.com',
    fn: 'Fred',
    sn: 'Frogg',
    phone: '01233xxxxx',
    hours: '80',
    rate: '90',
    amount: '800',
    vat: '100',
    agency: 'test',
    start: '08/06/2017',
    end: '10/06/2017',
    shipping: {
      addresses: [
        {
          id: '4567',
          area: 'asdaf'
        },
        {
          id: '3456',
          area: 'uio'
        }
      ]
    }
  },
  {
    email: 'alex@test.com',
    fn: 'Alex',
    sn: 'McPherson',
    phone: '01233xxxxx',
    hours: '90',
    rate: '30',
    amount: '900',
    vat: '120',
    agency: 'test',
    start: '08/06/2017',
    end: '10/06/2017',
    shipping: {
      addresses: [
        {
          id: '4567',
          area: 'asdaf'
        },
        {
          id: '5678',
          area: 'asdf'
        }
      ]
    }
  }
]
 

В идеале я хочу сгруппировать те, которые имеют одинаковое значение (shipping.addresses.id ) в собственный подмассив объектов. Ожидаемый результат.

 [
  {
    id: '1234',
    area: 'xzy',
    data: [
      {
        email: 'alex@test.com',
        fn: 'Alex',
        sn: 'McPherson',
        phone: '01233xxxxx',
        hours: '40',
        rate: '20',
        amount: '200',
        vat: '60',
        agency: 'test',
        start: '08/06/2017',
        end: '10/06/2017',
        shipping: {
          addresses: [
            {
              id: '1234',
              area: 'xzy'
            },
            {
              id: '2345',
              area: 'uhj'
            }
          ]
        }
      },
      {
        email: 'mike@test.com',
        fn: 'Mike',
        sn: 'Mann',
        phone: '01233xxxxx',
        hours: '50',
        rate: '70',
        amount: '500',
        vat: '90',
        agency: 'test',
        start: '08/06/2017',
        end: '10/06/2017',
        shipping: {
          addresses: [
            {
              id: '1234',
              area: 'xzy'
            },
            {
              id: '3456',
              area: 'uhj'
            }
          ]
        }
      }
    ]
  },
  {
    id: '2345',
    area: 'uhj',
    data: [
      {
        email: 'alex@test.com',
        fn: 'Alex',
        sn: 'McPherson',
        phone: '01233xxxxx',
        hours: '40',
        rate: '20',
        amount: '200',
        vat: '60',
        agency: 'test',
        start: '08/06/2017',
        end: '10/06/2017',
        shipping: {
          addresses: [
            {
              id: '1234',
              area: 'xzy'
            },
            {
              id: '2345',
              area: 'uio'
            }
          ]
        }
      }
    ]
  },
  {
    id: '3456',
    area: 'uio',
    data: [
      {
        email: 'mike@test.com',
        fn: 'Mike',
        sn: 'Mann',
        phone: '01233xxxxx',
        hours: '50',
        rate: '70',
        amount: '500',
        vat: '90',
        agency: 'test',
        start: '08/06/2017',
        end: '10/06/2017',
        shipping: {
          addresses: [
            {
              id: '1234',
              area: 'xzy'
            },
            {
              id: '3456',
              area: 'uio'
            }
          ]
        }
      },
      {
        email: 'fred@test.com',
        fn: 'Fred',
        sn: 'Frogg',
        phone: '01233xxxxx',
        hours: '80',
        rate: '90',
        amount: '800',
        vat: '100',
        agency: 'test',
        start: '08/06/2017',
        end: '10/06/2017',
        shipping: {
          addresses: [
            {
              id: '4567',
              area: 'asdaf'
            },
            {
              id: '3456',
              area: 'uio'
            }
          ]
        }
      }
    ]
  }
]
 

Я могу сгруппировать входной массив, используя определенный атрибут, используя определенный ключ (код ниже), но, похоже, я не могу разобраться с использованием массива на основе ключа, который сам по себе является массивом.

 Array.from(
    data.reduce( 
        (acc, o) => (acc.get(o.email).push(o), acc),
        new Map(data.map( o => [o.email, []] ))
    ), ([key, value]) => value
)
 

Ответ №1:

Вы можете преобразовать data массив в объект, используя shipping.addresses.id ключи as, и вернуть массив, используя Object.values() . Вам нужно будет перебирать addresses массив каждого объекта и создавать записи для каждого id по мере их обнаружения и переходить к этим записям для последующих элементов с тем же id значением.

 const byAddressId = Object.values(
  data.reduce((a, o) => {
    o.shipping.addresses.forEach(({id, area}) => {
      a[id]  = {...a[id] ?? {id: id, area: area, data: []}};
      a[id]['data'].push({...o});
    });
    return a;  
  }, {}));
 
 const data = [{"email": "alex@test.com","fn": "Alex","sn": "McPherson","phone": "01233xxxxx","hours": "40","rate": "20","amount": "200","vat": "60","agency": "test","start": "08/06/2017","end": "10/06/2017","shipping": {   "addresses": [  { "id": "1234", "area": "xzy"  },  { "id": "2345", "area": "uhj"  }   ]}},{"email": "mike@test.com","fn": "Mike","sn": "Mann","phone": "01233xxxxx","hours": "50","rate": "70","amount": "500","vat": "90","agency": "test","start": "08/06/2017","end": "10/06/2017","shipping": {   "addresses": [  { "id": "1234", "area": "xzy"  },  { "id": "3456", "area": "uio"  }   ]}},{"email": "fred@test.com","fn": "Fred","sn": "Frogg","phone": "01233xxxxx","hours": "80","rate": "90","amount": "800","vat": "100","agency": "test","start": "08/06/2017","end": "10/06/2017","shipping": {   "addresses": [  { "id": "4567", "area": "asdaf"  },  { "id": "3456", "area": "uio"  }   ]}},{"email": "alex@test.com","fn": "Alex","sn": "McPherson","phone": "01233xxxxx","hours": "90","rate": "30","amount": "900","vat": "120","agency": "test","start": "08/06/2017","end": "10/06/2017","shipping": {   "addresses": [  { "id": "4567", "area": "asdaf"  },  { "id": "5678", "area": "asdf"  } ]}}];

// return array of Object.values from the accumulator
const byAddressId = Object.values(
  // reduce the data array into an object with shipping.addresses.id as keys
  data.reduce((a, o) => {
    // iterate over all addresses for each element
    o.shipping.addresses.forEach(({id, area}) => {
      // check if an id entry exists, otherwise create one
      a[id]  = {...a[id] ?? {id: id, area: area, data: []}};
      // push the object to the data array of the id object
      a[id]['data'].push({...o});
    });
    return a;  
  }, {}));

console.log(byAddressId); 

При этом вы можете использовать этот же метод, чтобы сэкономить два map() вызова по сравнению с group by email примером, который вы включили в свой вопрос.

 const byEmail = Object.values(
    data.reduce((a, o) => (a[o.email] = [...a[o.email] ?? [], {...o}], a), {}));