0

let's say i wanted refactor this approach, rather than using a for loop, is there any other way to do this dynamically

  //to apply filter functions in one loop
  const booleanFilterFunctions = [
    (launchData) =>
      launchData.launch_year === filterState.year ||
      filterState.year === "0000",
    (launchData) => launchData.launch_success === filterState.launch,
    (launchData) => launchData.land_success === filterState.landing,
  ];
return (
 <div className={styles.grid}>
          {data
            .filter((launchData) => {
              let result = true;
              for (let i of booleanFilterFunctions)
                result = result && i(launchData);
              return result;
            })
            .map((launchData) => (
              <a key={launchData.flight_id} className={styles.card}>
                <img alt={launchData.links.mission_patch_small} loading="lazy" src={launchData.links.mission_patch_small}></img>

                <h3>
                  {launchData.mission_name} #{launchData.flight_number}
                </h3>
              </a>
            ))}
        </div>);
Ishank Sharma
  • 79
  • 1
  • 7
  • I think you want Code Review: https://codereview.stackexchange.com/ – Jared Farrish Sep 26 '20 at 15:46
  • hmm maybe but kind of a blurred line between review and asking for a better approach, this certainly isn't for work :p – Ishank Sharma Sep 26 '20 at 17:17
  • You might want to check out composing predicates using `And = (f, g) => x => f(x) && g(x)` and `Or = (f, g) => x => f(x) || g(x)` like I describe in [this answer](https://stackoverflow.com/a/53732612/3297291) – user3297291 Sep 29 '20 at 08:20

2 Answers2

1

You can use the array every method for this:

data.filter(launchData =>
  booleanFilterFunctions.every(i => i(launchData))
)
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • i'm looking for a composition kind of way, every would take only 1 function, maybe i shoul have been more clear, what i want to do is getList of available functions let's say from a user, apply them all in one go – Ishank Sharma Sep 26 '20 at 17:16
  • @IshankSharma No, the `every` in my answer is called on that list of available functions (`booleanFilterFunctions`) and runs them all (`i(launchData)`). It does exactly the same thing as the loop in your question. – Bergi Sep 26 '20 at 17:34
0

Instead of applying 3 filter functions one after the another, have you considered chaining the internal boolean logic? From the logic it looks like you want to show the content only when the 3 conditions are fulfilled. You can do something like this..

const filterData = (launchData) =>
  (launchData.launch_year === filterState.year ||
    filterState.year === "0000") &&
  launchData.launch_success === filterState.launch &&
  launchData.land_success === filterState.landing;


return (
 <div className={styles.grid}>
          {data
            .filter(filterData)
            .map((launchData) => (
              <a key={launchData.flight_id} className={styles.card}>
                <img alt={launchData.links.mission_patch_small} loading="lazy" src={launchData.links.mission_patch_small}></img>

                <h3>
                  {launchData.mission_name} #{launchData.flight_number}
                </h3>
              </a>
            ))}
        </div>);

You should be able to go even further on optimization and can avoid the extra iteration through data, by putting the condition directly on the content. Something like,

const shouldShowContent = (launchData) =>
  (launchData.launch_year === filterState.year ||
    filterState.year === "0000") &&
  launchData.launch_success === filterState.launch &&
  launchData.land_success === filterState.landing;


return (
 <div className={styles.grid}>
          {data
            .map((launchData) => shouldShowContent(launchData) && (
              <a key={launchData.flight_id} className={styles.card}>
                <img alt={launchData.links.mission_patch_small} loading="lazy" src={launchData.links.mission_patch_small}></img>

                <h3>
                  {launchData.mission_name} #{launchData.flight_number}
                </h3>
              </a>
            ))}
        </div>);
  • yes i have but that would default the purpose of question, to allow more dynamic behaviour – Ishank Sharma Sep 26 '20 at 17:12
  • No, you should not "*put the condition directly on the content*". That will render an array of `false` values. – Bergi Sep 26 '20 at 17:35
  • @Bergi right. But react doesn't render "false" value. That means it'll only render non-falsy elements. Let me know if I'm missing anything here. – ankit sinha Sep 26 '20 at 17:51
  • Yes, react might filter them out for you, but it's still not a good practice to mix different types in the same array. The `.filter(shouldShowContent)` call is much cleaner (and there's hardly potential for micro-optimisation). At least use something like `shouldShowContent(launchData) ? : null` if you think it's necessary. – Bergi Sep 26 '20 at 17:54
  • Hmm.. right that makes sense to not have react render different types from the same array. Thanks! – ankit sinha Sep 26 '20 at 17:57