I have this sample data and I wanted to filter it where the colors are less than 20 or equal to 20:
const data = [
{ name: "Item1", colors: { green: 8 } },
{ name: "Item2", colors: { green: 7, black: 6 } },
{ name: "Item3", colors: { green: 20, yellow: 31, pink: 36 } },
{ name: "Item4", colors: { black: 39, red: 21 } },
];
I recreated this in codesandbox: https://codesandbox.io/s/magical-margulis-yy1moz?file=/src/App.js
I tried this:
const data = [{colors: { green: 8 },name: "Item1"},{colors: { Black: 6, Green: 7 },name: "Item2"},{colors: { Green: 20, Yellow: 31, Pink: 36 },name: "Item2"},{colors: { Black: 39, Red: 21 },name: "Item4"}];
const newData = data.filter((item) => {
return Object.entries(item.colors).filter((c) => c[1] < 20);
});
console.log(newData);
It does not correctly filter. I can still see all of the items even if they are more than 20
The expected output would be to show the filtered data:
Item1, green: 8
Item2, Black: 6, Green: 7,
Item3, Green: 20
CodePudding user response:
A possible approach was to reduce
the given data structure twice.
The 1st reduce cycle mimics filter functionality. Filtering directly does not work since items might only partially meet the criteria of featuring color values which are lower than or equal to 20
.
Thus one needs to (re)create/assemble such an item with just the matching key-value pairs which is taken care of by the 2nd reduce tasks where the Object.entries
of a color item are getting processed, and the new color item programmatically gets aggregated via Object.assign
.
Back again within the 1st reduce cycle the final decision whether to collect (not quite filter) an item is made upon the just created color object. In case this object features at least one key it is a valid object; thus a final item can be created from the original item's data and the new color object.
const data = [
{ name: "Item1", colors: { green: 8 } },
{ name: "Item2", colors: { green: 7, black: 6 } },
{ name: "Item3", colors: { green: 20, yellow: 31, pink: 36 } },
{ name: "Item4", colors: { black: 39, red: 21 } },
];
console.log(
data
.reduce((result, { name, colors, ...rest }) => {
// (try to) create a new color item
// with all the key-value pairs where
// `value` meets the OP's criteria.
const newColorItem = Object
.entries(colors)
.reduce((item, [key, value]) => {
if (value <= 20) {
Object.assign(item, { [key]: value });
}
return item;
}, {});
// for every created and valid new color item
// push a newly created item into the `result`
// array where this item's data structure equals
// the structure of the currently processed
// original item.
if (Object.keys(newColorItem).length >= 1) {
result
.push({
name,
colors: newColorItem,
...rest,
});
}
return result;
}, [])
)
.as-console-wrapper { min-height: 100%!important; top: 0; }
CodePudding user response:
i think think filter wont do the job here because is an object instead of an array we can do a combination of reduce and more methods to achieve this, i also spotted typos in your data so i fixed as well
const data = [
{
colors: { green: 8 },
name: 'Item1',
},
{
colors: { black: 6, green: 7 },
name: 'Item2',
},
{
colors: { green: 20, yellow: 31, pink: 36 },
name: 'Item3',
},
{
colors: { black: 39, red: 21 },
name: 'Item4',
},
];
const res = data.reduce((acc, v) => {
const colors = Object.keys(v.colors).filter(c => v.colors[c] <= 20);
if (!acc[v.name] && colors.length) {
const values = {};
colors.forEach(c => {
values[c] = v.colors[c];
});
acc[v.name] = values;
}
return acc;
}, {});
console.log(res);
.as-console-wrapper { min-height: 100%!important; top: 0; }
CodePudding user response:
You can filter on color value using array#filter
and create a new colors
object and push it in result using array#reduce
.
const data = [ { colors: { green: 8 }, name: "Item1" }, { colors: { Black: 6, Green: 7 }, name: "Item2" }, { colors: { Green: 20, Yellow: 31, Pink: 36 }, name: "Item2" }, { colors: { Black: 39, Red: 21 }, name: "Item4" } ],
result = data.reduce((r, o) => {
const colors = Object.entries(o.colors).filter(([,val]) => val <= 20);
if(colors.length) {
r.push({ colors: Object.fromEntries(colors), name: o.name });
}
return r;
},[]);
console.log(result);
CodePudding user response:
Try this
const data = [{
colors: {
green: 8
},
name: "Item1"
},
{
colors: {
Black: 6,
Green: 7
},
name: "Item2"
},
{
colors: {
Green: 20,
Yellow: 31,
Pink: 36
},
name: "Item2"
},
{
colors: {
Black: 39,
Red: 21
},
name: "Item4"
}
];
const filtered = data.map(d => ({ ...d,
colors: Object.fromEntries(Object.entries(d.colors).filter(([_, v]) => v <= 20))
})).filter(d => Object.keys(d.colors).length > 0)
console.log(filtered)
CodePudding user response:
as filter
returns the array (an empty array is a truthy value) so your inner filter is always true hence it is giving all the data.
const data = [
{
color: { green: 8 },
name: "Item1"
},
{
color: { Black: 6, Green: 7 },
name: "Item2"
},
{
color: { Green: 20, Yellow: 31, Pink: 36 },
name: "Item3"
},
{
color: { Black: 39, Red: 21 },
name: "Item4"
}
];
const newData = data.flatMap(item => {
const filteredColors = Object.entries(item.color).filter(c => c[1] <= 20);
return filteredColors.length ? { name: item.name, colors: filteredColors } : [];
});
console.log(newData);
CodePudding user response:
This should work
const data = [
{ name: "Item1", colors: { green: 8 } },
{ name: "Item2", colors: { green: 7, black: 6 } },
{ name: "Item3", colors: { green: 20, yellow: 31, pink: 36 } },
{ name: "Item4", colors: { black: 39, red: 21 } },
];
const newData = [];
data.forEach((item) => {
Object.values(item.colors).forEach((color) => {
if (color <= 20) {
newData.push(item);
}
});
});
const newDataUnique = [...new Set(newData)];
console.log(newDataUnique);
.as-console-wrapper { min-height: 100%!important; top: 0; }