Home > Net >  Foreach 'attributes' in 'obj' find only 'Silver' and 'White'
Foreach 'attributes' in 'obj' find only 'Silver' and 'White'

Time:09-02

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.

  • Related