Search code examples
javascriptstring-literals

How to pass string literal into a filter function in javascript


I want to pass a string literal to a filter function.

the outcome should be

filter = data.filter(o => o.tag.toLowerCase().indexOf(("website").toLowerCase()) != -1 &&
          o.tag.toLowerCase().indexOf(("phone").toLowerCase()) != -1

What I am currently doing is

  • Use a for loop to get all the tags in the array
  • form a string query and perform a filter in the array
  • the problem is when the filter is passed, it returns everything, meaning the string literal in not working
for (let i = 0; i < values.length; i++) {
          if (i + 1 >= values.length) {
            query = query.concat(` o.tag.toLowerCase().indexOf(("${values[i]}").toLowerCase()) != -1 `);
          } else {
            query = query.concat(` o.tag.toLowerCase().indexOf(("${values[i]}").toLowerCase()) != -1 && `);
          }

        }
let filter = data.filter(o => `${query}`);
        debugger;


A sample snippet of what I am trying to filter. I want to make this filter function to be dynamic

data=[{name:"fpl.xlsx",author:"hello",hits:6,date:"2020-01-01",tag:"logo,website"}
,{name:"corporate.pptx",author:"hellob",hits:1,date:"2020-02-01",tag:"logo"},
{name:"index.html",author:"hellob",hits:7,date:"2020-02-02",tag:"logo,abc"}

]

let filter=[];
filter=data.filter(o=>o.tag.indexOf("logo")!=-1 && o.tag.indexOf("abc")!=-1)

console.log(filter);


Solution

  • If you want to make sure all of the values match an item that's in tag, then you can use Array#every to do it.

    const arr1 = ["aardvark", "ant", "alien"];
    const arr2 = ["apple", "audi", "bcoccoli"];
    
    const startsWithA = word => word[0] === "a";
    
    console.log(arr1.every(startsWithA));
    console.log(arr2.every(startsWithA));

    Furthermore, the String#includes method can be used instead of .indexOf since it directly returns a boolean.

    const word = "applesauce";
    
    console.log(word.includes("apple"));
    console.log(word.includes("sauce"));

    So we get this:

    const data = [{name:"fpl.xlsx",author:"hello",hits:6,date:"2020-01-01",tag:"logo,website"}
    ,{name:"corporate.pptx",author:"hellob",hits:1,date:"2020-02-01",tag:"logo"},
    {name:"index.html",author:"hellob",hits:7,date:"2020-02-02",tag:"logo,abc"}]
    
    const values = ["logo", "abc"];
    
    let result = data
      .filter(
        ({tag}) => values.every(
            value => tag.toLowerCase()
                .includes(value.toLowerCase())
         ) 
      )
      
    console.log(result);

    However, since tag contains a comma separated list, you might get false positives:

    const tag1 = "foobar";
    const tag2 = "foo,bar";
    
    const values = ["foo", "bar"];
    
    console.log(values.every(value => tag1.includes(value)));
    console.log(values.every(value => tag2.includes(value)));

    A tag might be a word composed of several others and match, whereas you only want to do a full match. You can use String#split to separate the tags, and you simply have to find if the two arrays overlap:

    const tag1 = "foobar";
    const tag2 = "foo,bar";
    
    const values = ["foo", "bar"];
    
    const arrayOfTag1 = tag1.split(",");
    const arrayOfTag2 = tag2.split(",");
    
    console.log(values.every(value => arrayOfTag1.includes(value)));
    console.log(values.every(value => arrayOfTag2.includes(value)));

    Sets can be used to reduce the complexity of the whole lookup in which case the filtering can look like this:

    const data = [{name:"fpl.xlsx",author:"hello",hits:6,date:"2020-01-01",tag:"logo,website"}
    ,{name:"corporate.pptx",author:"hellob",hits:1,date:"2020-02-01",tag:"logo"},
    {name:"index.html",author:"hellob",hits:7,date:"2020-02-02",tag:"logo,abc"}]
    
    const values = ["logo", "abc"];
    
    let result = data
      .filter(
        ({tag}) => {
          const lookupTags = new Set(
            tag
              .split(",")
              .map(x => x.toLowerCase())
          );
        
          return values.every(value => lookupTags.has(value.toLowerCase())
         )
       })
      
    console.log(result);