Я пытаюсь решить следующий случай без использования цикла for... Это означает, что я пытаюсь использовать только фильтр, уменьшение, карту или их комбинацию.
Я получил массив объектов:
const items =
[ { label: 1, count: 22, isRefined: false }
, { label: 2, count: 10, isRefined: false }
, { label: 3, count: 3, isRefined: false }
, { label: 4, count: 1, isRefined: false }
]
const products =
[ { Locations: [{ LocationType: 1 } , { LocationType: 2 } , { LocationType: 3 } ]}
, { Locations: [{ LocationType: 1 } , { LocationType: 3 } ]}
, { Locations: [{ LocationType: 1 } , { LocationType: 3 } , { LocationType: 4 } ]}
, { Locations: [{ LocationType: 1 } ]}
, { Locations: [{ LocationType: 1 } , { LocationType: 2 } , { LocationType: 3 } ]}
]
Дело следующее:
Мне нужно перезаписать внутренний ключ счетчика объектов каждого элемента значением соответствующих продуктов с объектами, в которых объекты Locations содержат соответствующий тип Location для метки объекта items...
Например, вывод здесь должен быть
const output =
[ { label: 1, count: 5, isRefined: false }
, { label: 2, count: 2, isRefined: false }
, { label: 3, count: 4, isRefined: false }
, { label: 4, count: 1, isRefined: false }
]
Я полностью застрял в попытке найти решение без использования цикла for или любого другого цикла for, помощь будет очень признательна! Спасибо
Разве label: 3
не должно быть count
из 4
вместо 3
? Пожалуйста, проверьте свои примеры заранее
«Массив (#)» недействителен. Как рекомендовал Ибрагим, нам нужны действительные ориентиры, прежде чем мы сможем реально помочь.
Ваш инструктор, скорее всего, пытается указать вам на использование reduce
или map
.
Извините, я исправил ошибки. Под циклом for я подразумеваю конкретно for (let i = 0; i < someNum; i++)```, forEach, forOf. Да, он сказал, что предпочтительнее использовать сокращение, карту или фильтр.
Пожалуйста, проверьте мой ответ, не используются for
/forEach
/forOf
, только map
и filter
.
Сначала подсчитайте количество (количество раз, когда каждый тип местоположения появляется в массиве products
), желательно в виде объекта, где ключи — это типы местоположения, а значения — количество. Для этого простой reduce
/forEach
выполнит эту работу следующим образом:
let counts = products.reduce((counts, locs) => {
locs.Locations.forEach(loc =>
counts[loc.LocationType] = (counts[loc.LocationType] || 0) + 1
);
return counts;
}, {});
Затем просто map
массив items
в новый массив, где вы неглубоко копируете объекты, используя синтаксис распространения, и обновляете свойство count
, используя объект counts
, рассчитанный ранее:
const newItems = items.map(item => ({
...item,
count: counts[item.label] || 0
}));
А если вы хотите вместо этого изменить сами исходные объекты, то простого forEach
будет достаточно:
items.forEach(item => item.count = counts[item.label] || 0);
Часть || 0
является резервным значением на случай, если элемент не имеет типов местоположения в массиве products
, и в этом случае counts[item.label]
будет undefined
, а вместо него будет использоваться 0
. Это называется оценкой короткого замыкания.
Демо:
const items = [{label: 1, count: 22, isRefined: false}, {label: 2, count: 10, isRefined: false}, {label: 3, count: 3, isRefined: false},{label: 4, count: 1, isRefined: false}];
const products = [{Locations:[{LocationType:1},{LocationType:2},{LocationType:3}]},{Locations:[{LocationType:1},{LocationType:3}]},{Locations:[{LocationType:1},{LocationType:3},{LocationType:4}]},{Locations:[{LocationType:1}]},{Locations:[{LocationType:1},{LocationType:2},{LocationType:3}]}];
let counts = products.reduce((counts, locs) => {
locs.Locations.forEach(loc =>
counts[loc.LocationType] = (counts[loc.LocationType] || 0) + 1
);
return counts;
}, {});
const newItems = items.map(item => ({
...item,
count: counts[item.label] || 0
}));
console.info(newItems);
Большое спасибо! Работает как шарм, я проверю у него, приемлем ли forEach. :)
@BranislavBrnjoš Добро пожаловать! Я рад, что смог помочь :)
Простой и элегантный.
ОБНОВЛЕНИЕ
Спасибо @plichard за предложение в комментариях ниже использовать метод some()
и деструктуризацию объекта в обратном вызове filter
. Это действительно аккуратно!
const output = items.map(item => {
const matches = (location) => location.LocationType == item.label;
const filtered = products.filter(({ Locations }) => Locations.some(matches));
return { ...item, count: filtered.length };
});
some()
кажется более подходящим, чем принуждение логического значения из фильтра. products.filter(({ Locations }) => Locations.some(matches));
@pilchard Не думал об этом, так даже лучше, спасибо!
В случае, если forEach не подходит, вот еще одно решение, которое использует карту, уменьшение и фильтрацию.
const result = items.map(d => {
const locationCount = products.reduce((count, p) => count + p.Locations.filter(l => l.LocationType == d.label).length, 0);
let item = Object.assign({}, d);
item.count = locationCount;
return item;
})
Например:
const items =
[ { label: 1, count: 22, isRefined: false }
, { label: 2, count: 10, isRefined: false }
, { label: 3, count: 3, isRefined: false }
, { label: 4, count: 1, isRefined: false }
]
const products =
[ { Locations: [{ LocationType: 1 } , { LocationType: 2 } , { LocationType: 3 } ]}
, { Locations: [{ LocationType: 1 } , { LocationType: 3 } ]}
, { Locations: [{ LocationType: 1 } , { LocationType: 3 } , { LocationType: 4 } ]}
, { Locations: [{ LocationType: 1 } ]}
, { Locations: [{ LocationType: 1 } , { LocationType: 2 } , { LocationType: 3 } ]}
]
const result = items.map(d => {
const locationCount = products.reduce((count, p) => count + p.Locations.filter(l => l.LocationType == d.label).length, 0);
let item = Object.assign({}, d);
item.count = locationCount;
return item;
})
console.info(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Ваше решение нарушает целостность items
(их значения счетчика изменены)
@MisterJojo исправлено, спасибо за указание
одна строчка: return Object.assign({}, d, {count: locationCount})
мой путь...
const countP = v => products.filter(p=>p.Locations.some(x=>x.LocationType===v)).length
const output = items.map(el=>({...el, count: countP(el.label)}))
доказательство:
const items =
[ { label: 1, count: 22, isRefined: false }
, { label: 2, count: 10, isRefined: false }
, { label: 3, count: 3, isRefined: false }
, { label: 4, count: 1, isRefined: false }
]
const products =
[ { Locations: [{ LocationType: 1 } , { LocationType: 2 } , { LocationType: 3 } ]}
, { Locations: [{ LocationType: 1 } , { LocationType: 3 } ]}
, { Locations: [{ LocationType: 1 } , { LocationType: 3 } , { LocationType: 4 } ]}
, { Locations: [{ LocationType: 1 } ]}
, { Locations: [{ LocationType: 1 } , { LocationType: 2 } , { LocationType: 3 } ]}
]
const countP = v => products.filter(p=>p.Locations.some(x=>x.LocationType===v)).length
const output = items.map(el=>({...el, count: countP(el.label)}))
console.info( output )
.as-console-wrapper { max-height: 100% !important; top: 0; }
.as-console-row { background-color: #87f1f1; }
.as-console-row::after { display:none !important; }
Под циклом
for
вы имеете в виду конкретноfor (let i = 0; i < someNum; i++)
или вы имеете в виду любую форму итерации? Как насчет.map()
или.forEach()
в массиве?