1

I have a nest list of Objects in JavaScript and I want to filter them using a 'search string' and a property value.

I want to only collect the Categories that have Children who are not Hidden and contain at least one profile with the search keyword in it's name. I'm not quite sure how to write the function itself, utilizing the bells and whistles of JavaScript.

Below is pseudo code

initial list:

categories = [
   {
      "category":"Players",
      "children":[
         {
            "profiles":[
               {
                  "name":"Kevin"
               },
               {
                  "name":"Kevin Young"
               },
               {
                  "name":"Kevin Old"
               }
            ],
            "isHidden":false
         },
         {
            "profiles":[
               {
                  "name":"Mike"
               },
               {
                  "name":"Mike Baby"
               }
            ],
            "isHidden":false
         },
         {
            "profiles":[
               {
                  "name":"Joe Old"
               }
            ],
            "isHidden":false
         }
      ]
   },
   {
      "category":"Teams",
      "children":[
         {
            "profiles":[
               {
                  "name":"Cowboys"
               }
            ],
            "isHidden":true
         },
         {
            "profiles":[
               {
                  "name":"Steelers"
               }
            ],
            "isHidden":false
         }
      ]
   }
]

pseudo function:

filterList: function(): Categories[] {
   return categories.filter((cat: Category): boolean => {
      // loop through each categories list of children

      // if child is hidden skip the child item
      // if child's profiles do not contain the word 'old' (Caseinsensitive) skip child

      // lastly do not return a category in the return if there are no children
      // after filtering them based on the conditions above

      // if category contains one or more children after children
   });
 }

results of function:

categories = [
   {
      "category":"Players",
      "children":[
         {
            "profiles":[
               {
                  "name":"Kevin"
               },
               {
                  "name":"Kevin Young"
               },
               {
                  "name":"Kevin Old"
               }
            ],
            "isHidden":false
         },
         {
            "profiles":[
               {
                  "name":"Joe Old"
               }
            ],
            "isHidden":false
         }
      ]
   }
]
JokerMartini
  • 5,674
  • 9
  • 83
  • 193

2 Answers2

0

This specifically answers your question.. filter works by not returning an index of an array when you return false for that index

Also, I copy the object instead of using it directly BECAUSE while filtering I edit properties in the deeper levels of the nest(acting as the filter in the deep levels)

//I am copying the array taken in to avoid data loss from original object

function doIt(arr,word){
  try{arr=JSON.parse(JSON.stringify(arr))}
  catch(err){throw Error(err)}
  return arr.filter(a=>{
    var toReturn=true
    a.children.forEach(b=>{
      if(!b.profiles.length||b.isHidden){return toReturn=false} //filters out the lvl 1 elements not meeting requirements and if it is to be filtered out, unnecesary steps below are avoided
      var tempArr=[] //for "whitelisting" since only the indexes that include [word] are to be apart of the profiles
      b.profiles.forEach((c,i)=>{ //checking and valid results are pushed into the array
        if(c.name.toLowerCase().includes(word)){tempArr.push(c)}
      })
      b.profiles=tempArr //linking complete
      if(!tempArr.length){delete(b.profiles)} //if nothing in an array has a valid value, removal
    })
    return toReturn
  })
}

console.log(doIt(categories,'old'))
<script>
categories = [ //it's a global on purpose
   {
      "category":"Players",
      "children":[
         {
            "profiles":[
               {
                  "name":"Kevin"
               },
               {
                  "name":"Kevin Young"
               },
               {
                  "name":"Kevin Old"
               }
            ],
            "isHidden":false
         },
         {
            "profiles":[
               {
                  "name":"Mike"
               },
               {
                  "name":"Mike Baby"
               }
            ],
            "isHidden":false
         },
         {
            "profiles":[
               {
                  "name":"Joe Old"
               }
            ],
            "isHidden":false
         }
      ]
   },
   {
      "category":"Teams",
      "children":[
         {
            "profiles":[
               {
                  "name":"Cowboys"
               }
            ],
            "isHidden":true
         },
         {
            "profiles":[
               {
                  "name":"Steelers"
               }
            ],
            "isHidden":false
         }
      ]
   }
]
</script>
The Bomb Squad
  • 4,192
  • 1
  • 9
  • 17
  • Dear @The Bomb Squad, the result of your function was wrong. It should return two items in the "children" array but returns 3 items. The name of the profiles in one of them did not contain "old". – CyberEternal Feb 04 '21 at 20:27
  • @CyberEternal oh.. sry about that.. editing.... – The Bomb Squad Feb 04 '21 at 21:46
0

filterList function will be called with the two params. categories - the list of categories, and keyword - search keyword. So my function checking each category from the category list. For each category, I filtered the children's list by the isHidden=false parameter and also by the existing profile with the name, which contains the keyword. I also using the toLocaleLowerCase function for the Case-insensitive. And if the category has a children's, which meet the requirements, I pushed to the result[], and overwrite the children value for it. And finally, the function returning the result array with required categories

const filterList = (categories, keyword) => {
  const kwd = keyword.toLocaleLowerCase()
  const result = [];
  for (const cat of categories) {
    const children = cat.children.filter(i => !i.isHidden && i.profiles.some(j => j.name.toLocaleLowerCase().includes(kwd)));
    if (children.length) result.push({ ...cat, children })
  }
  return result;
}
CyberEternal
  • 2,259
  • 2
  • 12
  • 31
  • 1
    It's a good practice on Stackoverflow to explain *how* and *why* your code works. Can you explain to people *how* this works? Some people might know how it works but for the people who don't know it's a good idea to explain to them. – Reality Feb 04 '21 at 20:26
  • Sure @Reality. So filterList function will be called with the two params. categories - the list of categories, and keyword - search keyword. – CyberEternal Feb 04 '21 at 20:36
  • So my function checking each category from the category list. For each category, I filtered the children's list by the isHidden=false parameter and also by the existing profile with the name, which contains the keyword. I also using the toLocaleLowerCase function for the Case-insensitive. And if the category has a children's, which meet the requirements, I pushed to the result[], and overwrite the children value for it. And finally, the function returning the result array with required categories – CyberEternal Feb 04 '21 at 20:36
  • @CyberEternal your answer when run has names that don't include old – The Bomb Squad Feb 04 '21 at 22:24
  • @CyberEternal I did.. `filterList(categories, 'old')` returns `[{"category":"Players","children":[{"profiles":[{"name":"Kevin"},{"name":"Kevin Young"},{"name":"Kevin Old"}],"isHidden":false},{"profiles":[{"name":"Joe Old"}],"isHidden":false}]}]`.. you can see several names that exist and **DON'T HAVE** the keyword – The Bomb Squad Feb 05 '21 at 10:44
  • Dear @The Bomb Squad, please look to the success response example(results of function:) provided by JokerMartini, and please double-check the question requirements. Thanks! – CyberEternal Feb 05 '21 at 11:35