DEV Community

Cover image for Making the Filter
Bruno Noriller
Bruno Noriller

Posted on • Originally published at Medium

Making the Filter

Picking up from where we were in the Parser:

MyBasicParser(
  `some random string something "quoted here" not here 'here again' tag:value something alone: and other:tag :value`
);

// it returns:
/* 
    {
      singleValues:  [ 'some','random','string','something','not','here','something','alone:','and',':value' ],
      quotedValues: [ 'quoted here', 'here again' ],
      tagValues: [['tag', 'value'], ['other', 'tag']]
    }
*/
Enter fullscreen mode Exit fullscreen mode

Now... how do we use it?

Here's a draft of what we need:

function MyBasicFilter(arrayToBeFiltered) {
    function doFilter(parsedObject) {
        return arrayToBeFiltered.filter(
            (item) => FilterMagic({ item, parsedObject })
        );
    }

    function FilterMagic({ item, parsedObject }){'???'}

    return {
        filter: (parsedObject) => doFilter(parsedObject)
    };
}

// To use this:
const myArray = [/* ... values ... */];
const basicFilter = MyBasicFilter(myArray);

const parsedSearch = MyBasicParser(/* that big string */);
const filterResult = basicFilter.filter(parsedSearch);
Enter fullscreen mode Exit fullscreen mode

Here we will use a closure, just so we can prepare the function and then just call it afterwards.

Filter Magic

For that we will be needing:

function FilterMagic({
  item,
  parsedObject,
}) {
  // check the single values
  // check the quoted values
  // check the tag values
  return boolean;
}
Enter fullscreen mode Exit fullscreen mode

While writing this, I've made it in a way I didn't even though about using in EasyFilter!

function FilterMagic({ item, parsedObject }) {
  // this concatenates all strings in the item
  const stringValue = Object.values(item).join(' ');

  // using an object we avoid having to use a switch statement
  // the code is more clear and easier to read
  // and should we need to add more filters we can add them here in a simple way
  const myMagicFunctions = {
    // this is basically the "base case", it will check if the baseString have the values we are looking for with Regex using the case insensitive flag
    // we pass the stringValue of the item as default, but doing this allows other functions to override it
    singleValues: (string, baseString = stringValue) => Boolean(baseString.match(RegExp(string, 'i'))),
    // for the quoted values, we split them at the space and then call the sibling function "singleValues" for each value
    // and by using the "every" function we check if all the substrings are found
    // we could also had just called the "singleValues" function without splitting the string
    // but that would mean the it would check if it matched the exact string in order
    // it would look like this:
    // quotedValues: (string) => myMagicFunctions.singleValues(string),
    quotedValues: (string) => string.split(' ').every((subString) => myMagicFunctions.singleValues(subString)),
    // finally, the tags we spread the tag/value and override the default baseString of "singleValues" with the value of the tag
    tagValues: ([tag, value]) => myMagicFunctions.singleValues(value, item[tag]),
  }

  // we break down the parsedObject and with "some" check if any of the single, quoted or tag values have any match
  return Object.entries(parsedObject).some(
    ([parsedType, values]) => {
      // since each can hold an array, we loop through them using "some"
      // also, by using "some", it already stops in the first match!
      return values.some((payload) =>
        // and if this isn't magic, I don't know what is!
        // we use the types coming from the parsedObject,
        // if there's a match, we call the function that matches the type
        // and pass the payload (that can be either a string or a tuple)
        // if there's no match, it will return false by default
        myMagicFunctions[parsedType]?.(payload) ?? false
      );
    })
}

// If you want to play around with the FilterMagic
// here's a mockItem for you to test
const mockItem = {
  ghosts: "Are spooky?",
  regex: "More spooky?",
  happy: "Halloween"
};
// and here's a mockParsedObject
const mockParsedObject = {
  singleValues: ['spooky', 'spoolk'],
  quotedValues: ['spooky more', 'morr spook'],
  tagValues: [['happy', 'halloween']]
};
// invalidate some of the values to see what's called and what's not
// as is, it will stop at the very first one because "spooky" is in the item!
Enter fullscreen mode Exit fullscreen mode

The Final Result

function MyBasicFilter(arrayToBeFiltered) {
  function doFilter(parsedObject) {
    return arrayToBeFiltered.filter(
      (item) => FilterMagic({ item, parsedObject })
    );
  }

  function FilterMagic({ item, parsedObject }) {
    const stringValue = Object.values(item).join(' ');

    const myMagicFunctions = {
      singleValues: (string, baseString = stringValue) => Boolean(baseString.match(RegExp(string, 'i'))),
      quotedValues: (string) => string.split(' ').every((subString) => myMagicFunctions.singleValues(subString)),
      tagValues: ([tag, value]) => myMagicFunctions.singleValues(value, item[tag]),
    };

    return Object.entries(parsedObject).some(
      ([parsedType, values]) => {
        return values.some((payload) =>
          myMagicFunctions[parsedType]?.(payload) ?? false
        );
      });
  }

  return {
    filter: (parsedObject) => doFilter(parsedObject)
  };
}
Enter fullscreen mode Exit fullscreen mode

Fortunately, the filter is the easiest part...
Unfortunately... it's more straight forward code.

I'm happy that I've managed to do something totally different than what I've used in EasyFilter, but this is a way simpler version of that one...
Although I actually might think about using what we did here in there!

Hey... how about it?
Check if YOU can can do it... fork the project and then send a PR!


buy me a spook coffee

Cover Photo by Wade Austin Ellis on Unsplash

Top comments (0)