#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' }