UPDATE:
let obj = [{
id: 0,
rank: 5,
attributes: [
{ trait_type: 'Background', value: 'White' },
{ trait_type: 'Skin', value: 'Silver' },
{ trait_type: 'Special', value: 'Red' }
]
},{
id: 1,
rank: 3,
attributes: [
{ trait_type: 'Background', value: 'Green' },
{ trait_type: 'Skin', value: 'Cracked' },
{ trait_type: 'Special', value: 'None' }
]
},
{
id: 2,
rank: 1,
attributes: [
{ trait_type: 'Background', value: 'Red' },
{ trait_type: 'Skin', value: 'Cracked' },
{ trait_type: 'Special', value: 'None' }
]
}]
Get the obj array find the values eg 'Silver' and 'White' that are in the attributes then map out the id and rank of 'Silver' and 'White'
for example: Find ['Silver', 'White'] Return:
{2, 1}
eg id and rank of attributes value 'Silver' and 'White' is id = 2 and rank = 1
CodePudding user response:
You need to use not only .filter
but also .every
and .some
const findx = ['Silver', 'White']
const obj = [{attributes: [{ trait_type: 'Background', value: 'Pink' },{ trait_type: 'Skin', value: 'Silver' },{ trait_type: 'Clothing', value: 'Turtleneck' },{ trait_type: 'Eyes', value: 'Squinted' },{ trait_type: 'Hair', value: 'Mullet' },{ trait_type: 'Mouth', value: 'Freezie' },{ trait_type: 'Accessories', value: 'Piercings' },{ trait_type: 'Earbuds', value: 'White' },{ trait_type: 'Special', value: 'None' }]},{attributes: [{ trait_type: 'Background', value: 'Green' },{ trait_type: 'Skin', value: 'Cracked' },{ trait_type: 'Clothing', value: 'Biker Jacket' },{ trait_type: 'Eyes', value: 'Eyepatch' },{ trait_type: 'Hair', value: 'Straight' },{ trait_type: 'Mouth', value: 'Freezie' },{ trait_type: 'Accessories', value: 'None' },{ trait_type: 'Earbuds', value: 'White' },{ trait_type: 'Special', value: 'None' }]}];
const result = obj.filter(({ attributes }) =>
findx.every((term) =>
attributes.some(({ value }) => value === term)));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0 }
CodePudding user response:
Drill down to the attribute objects with two forEach()
loops, then use Array.includes()
to see which attribute objects ones have a value of 'Silver' or 'Gold', and push those into the results array:
let obj = [{
attributes: [
{ trait_type: 'Background', value: 'Pink' },
{ trait_type: 'Skin', value: 'Silver' },
{ trait_type: 'Clothing', value: 'Turtleneck' },
{ trait_type: 'Eyes', value: 'Squinted' },
{ trait_type: 'Hair', value: 'Mullet' },
{ trait_type: 'Mouth', value: 'Freezie' },
{ trait_type: 'Accessories', value: 'Piercings' },
{ trait_type: 'Earbuds', value: 'White' },
{ trait_type: 'Special', value: 'None' }
]
},{
attributes: [
{ trait_type: 'Background', value: 'Green' },
{ trait_type: 'Skin', value: 'Cracked' },
{ trait_type: 'Clothing', value: 'Biker Jacket' },
{ trait_type: 'Eyes', value: 'Eyepatch' },
{ trait_type: 'Hair', value: 'Straight' },
{ trait_type: 'Mouth', value: 'Freezie' },
{ trait_type: 'Accessories', value: 'None' },
{ trait_type: 'Earbuds', value: 'White' },
{ trait_type: 'Special', value: 'None' }
]
}]
const result = []
obj.forEach(obj =>
{
obj.attributes.forEach(obj =>
{
if(['Silver', 'White'].includes(obj.value))
{
result.push(obj)
}
})
})
console.log(result)
CodePudding user response:
Let's take a step back from filter
and break the logic down. We're only filtering on the top level array structure (I prefer to call this arr
rather than obj
). For each element in arr
/obj
, we need to establish that every()
element in findx
(I prefer to call this findAll
) needs to be in some()
element's value
. If we pass this test, the top-level filter returns true, otherwise false.
const findAll = ["Silver", "White"];
const arr = [
{
attributes: [
{trait_type: "Background", value: "Pink"},
{trait_type: "Skin", value: "Silver"},
{trait_type: "Clothing", value: "Turtleneck"},
{trait_type: "Eyes", value: "Squinted"},
{trait_type: "Hair", value: "Mullet"},
{trait_type: "Mouth", value: "Freezie"},
{trait_type: "Accessories", value: "Piercings"},
{trait_type: "Earbuds", value: "White"},
{trait_type: "Special", value: "None"},
],
},
{
attributes: [
{trait_type: "Background", value: "Green"},
{trait_type: "Skin", value: "Cracked"},
{trait_type: "Clothing", value: "Biker Jacket"},
{trait_type: "Eyes", value: "Eyepatch"},
{trait_type: "Hair", value: "Straight"},
{trait_type: "Mouth", value: "Freezie"},
{trait_type: "Accessories", value: "None"},
{trait_type: "Earbuds", value: "White"},
{trait_type: "Special", value: "None"},
],
},
];
const filtered = arr.filter(e =>
findAll.every(target =>
e.attributes.some(({value}) => value === target)
)
);
console.log(filtered);
filter
is usually suboptimal for establishing membership or testing a predicate. some
and every
are the best tools for this. Even if it did find an element, you'd have to call .length
on the array filter
allocates and returns to avoid the empty array []
being treated as truthy. Even with .length
to fix that bug, it's still a semantically poor choice, a waste of an array allocation and doesn't feature early bailout like some
and every
.
Stripping your attempt down to a simple example shows why filter
is wrong for checking membership:
const haystack = [1, 2, 3];
const needle = 42;
// wrong
if (haystack.filter(e => e === needle)) {
console.log(`[filter--bad] found ${needle} in ${haystack}`);
}
else {
console.log(`[filter--bad] didn't find ${needle} in ${haystack}`);
}
// correct but poor
if (haystack.filter(e => e === needle).length) {
console.log(`[filter--ok] found ${needle} in ${haystack}`);
}
else {
console.log(`[filter--ok] didn't find ${needle} in ${haystack}`);
}
// correct and good
if (haystack.some(e => e === needle)) {
console.log(`[some] found ${needle} in ${haystack}`);
}
else {
console.log(`[some] didn't find ${needle} in ${haystack}`);
}
I suggest const
rather than let
when you're not going to reassign these variables. It makes the code easier to read by communicating intent and helps avoid a class of bugs.