ES6 — Поиск данных во вложенных массивах

#javascript #arrays #ecmascript-6

#javascript #массивы #ecmascript-6

Вопрос:

В ES6, использующем find or filter , мне вполне удобно перебирать, чтобы найти элемент в массиве, используя значение.

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

Например, в этой структуре данных:

 products: [
  {
    id: 01,
    items: [
      {
        id: 01,
        name: 'apple'
      },
      {
        id: 02,
        name: 'banana'
      },
      {
        id: 03,
        name: 'orange'
      }
    ]
  },
  {
    id: 02,
    items: [
      {
        id: 01,
        name: 'carrot'
      },
      {
        id: 02,
        name: 'lettuce'
      },
      {
        id: 03,
        name: 'peas'
      }
    ]
  },
  {
    id: 03,
    items: [
      {
        id: 01,
        name: 'eggs'
      },
      {
        id: 02,
        name: 'bread'
      },
      {
        id: 03,
        name: 'milk'
      }
    ]
  }
]
 

Если я знаю name или id объекта milk , есть ли способ узнать идентификатор элемента, в который он вложен?

В настоящее время у меня есть это:

 products.find((product) => {
  product.find((prod) => {
    return prod.name === 'milk';
  });
});
 

Который возвращает только объект , содержащий milk .

Ответ №1:

Вы должны вернуть что — то из обратного вызова внешнего find . Фактически, для внутренней итерации вы не должны использовать find , а скорее some возвращает логическое значение для определения того, существует ли элемент, соответствующий условию, в arrray:

 products.find((product) => {
  return product.items.some((item) => {
//^^^^^^
    return item.name === 'milk';
  });
});
 

или короче:

 products.find(product => product.items.some(item => item.name === 'milk'));
 

Затем проверьте find , найдено ли что-то (нет null !) и получите его .id , результат должен быть 03 . Кроме того, вы можете filter использовать продукты, содержащие молоко, в качестве элемента, а затем сопоставить все результаты с их идентификатором:

 products.filter(product =>
  product.items.some(item => item.name === 'milk');
).map(product =>
  product.id
) // [03]
 

Комментарии:

1. Это имеет смысл — я раньше не видел some .. но используя это, как я мог бы вернуть идентификатор внешнего элемента? Например, для элемента milk содержащий элемент имеет идентификатор 3 . Спасибо!

2. просто обратите внимание, some что это должно произойти product.items , а не product напрямую

3. @Toby код, который дал вам Берги, найдет ваш объект, и все, что вам нужно сделать, это вызвать .id его: const yourId = products.find(product => product.items.some(item => item.name === 'milk')).id

4. что, если я хочу получить индекс массива?

5. @Kakajann Просто используйте findIndex вместо find !

Ответ №2:

Я знаю, что вы упоминаете ES6, но в этом случае (и если вы хотите вернуть внутренний объект) Я считаю, что лучше использовать for / of вместо map / reduce / find :

 for (let p of products) {
  for (let i of p.items) {
    if (i.name === 'milk') return i;
  }
}
 

Ответ №3:

Обновить

Как прокомментировал Рикардо Маримон, reduce не прерывается, поэтому он продолжает поиск по массиву, поэтому, имея это в виду, поскольку я не люблю использовать for loops императивный способ программирования, можно рано отказаться от reduce, изменив используемый массив, но это тоже было бы плохо, поэтому вместо этого еговместо этого можно сделать копию и изменить копию.

 // slice creates a copy of products
return products.slice(0).reduce((prev, product, i, arr) => {
    console.log(i);
    const findItem = prev || product.items.find(item => item.name === 'milk');
    if (typeof findItem !== 'undefined') arr.splice(1); // ejects early
    return findItem;
}, undefined);
 
 const products = [
  {id: 1, items: [
    {id: 1, name: 'apple'},
    {id: 2, name: 'banana'},
    {id: 3, name: 'orange'}
  ]},
  {id: 2, items: [
    {id: 1, name: 'carrot'},
    {id: 2, name: 'lettuce'},
    {id: 3, name: 'milk'}
  ]},
  {id: 3, items: [
    {id: 1, name: 'eggs'},
    {id: 2, name: 'bread'},
    {id: 3, name: 'peas'}
  ]}
];

const findItem = products.slice(0).reduce((prev, product, i, arr) => {
    console.log(i);
    const findItem = prev || product.items.find(item => item.name === 'milk');
    if (typeof findItem !== 'undefined') arr.splice(1); // ejects early
    return findItem;
}, undefined);

console.log(findItem); 

Старый

Принятый ответ не сделал этого за меня, потому что я хотел получить результат внутреннего поиска, используя оба, он всегда давал мне результат внешнего фильтра / поиска, и мне пришлось использовать результирующий массив, чтобы снова найти значение.

Поэтому вместо этого я использовал сокращение с помощью короткого замыкания, чтобы получить внутренний результат.

 // undefined as the initial value is necessary, otherwise it gets the first value of the array instead.

return products.reduce((prev, product) => prev || product.items.find(item => item.name === 'milk'), undefined);
 
 const products = [
  {id: 1, items: [
    {id: 1, name: 'apple'},
    {id: 2, name: 'banana'},
    {id: 3, name: 'orange'}
  ]},
  {id: 2, items: [
    {id: 1, name: 'carrot'},
    {id: 2, name: 'lettuce'},
    {id: 3, name: 'peas'}
  ]},
  {id: 3, items: [
    {id: 1, name: 'eggs'},
    {id: 2, name: 'bread'},
    {id: 3, name: 'milk'}
  ]}
];

console.log(products.reduce((prev, product) => prev || product.items.find(item => item.name === 'milk'), undefined)); 

Комментарии:

1. Спасибо, Густаво! Рыскал по Сети в поисках решения, чтобы получить внутреннюю находку. Ты потрясающая!

2. Имейте в виду, что этот способ продолжает поиск элемента даже после того, как он найден. Функция уменьшения не может сломаться. Решение от @ariel действительно ломается при нахождении.

3. Благодаря Рикардо Маримону, указав на эту проблему, я обновил ответ, чтобы исправить это, используя два «хакерских» способа использования reduce вместо циклов for .

Ответ №4:

Еще один разумный подход:

 products
  .map((category) => category.items)
  .flat()
  .find((product) => product.name === 'milk');
 

Ответ №5:

Чтобы получить элемент напрямую, без повторного удвоения, чтобы получить идентификатор / объект:

 const products = [
  { id: 01,
    items: [ { id: 01, name: 'apple' },
             { id: 02, name: 'banana'},
             { id: 03, name: 'orange'}]},
  { id: 02,
    items: [ { id: 01, name: 'carrot' },
             { id: 02, name: 'lettuce'},
             { id: 03, name: 'peas'   }]},
  { id: 03,
    items: [ { id: 01, name: 'eggs'  },
             { id: 02, name: 'bread' },
             { id: 03, name: 'milk'  }]}
 ]

    let found;
    for ( const category of products ){
        found = category.items.find( item => item.name == "milk" )
        if ( found ) break
    }
    console.log( found ) 
    // == { id: 3, name: 'milk' }