pacificclimate/climate-explorer-frontend

View on GitHub
src/core/selectors.js

Summary

Maintainability
A
0 mins
Test Coverage
// Functions used by selectors. Mostly common to several.
//
// Note: In this module, we follow a pure functional programming style wherever
// possible. In particular, this means (wherever possible):
//
//    - No side effects (pure functions)
//
//    - Iteratee-first, data-last methods
//      (see https://github.com/lodash/lodash/wiki/FP-Guide)
//
//    - Most methods are partially or fully curried.

import compact from "lodash/fp/compact";
import find from "lodash/fp/find";
import flatMap from "lodash/fp/flatMap";
import flow from "lodash/fp/flow";
import get from "lodash/fp/get";
import flatten from "lodash/fp/flatten";
import map from "lodash/fp/map";
import reduce from "lodash/fp/reduce";
import assign from "lodash/fp/assign";
import _ from "lodash";
import filter from "lodash/fp/filter";
import sortBy from "lodash/fp/sortBy";

// Default-value replacers for selectors for model, emissions scenario,
// and variable.

// Warning: This will fail, with an infinite update loop, if there is no
// enabled option.
const fallback = (options) => find((opt) => !opt.isDisabled)(options);

export const findModelNamed = (model_id) => (options) =>
  find({ value: { representative: { model_id } } })(options) ||
  fallback(options);

export const findScenarioIncluding = (scenarios) => (options) =>
  scenarios
    .map((s) =>
      find(
        (opt) =>
          opt.value.representative.experiment.includes(s) && !opt.isDisabled,
      )(options),
    )
    .find((e) => typeof e != "undefined") || fallback(options);

export const findVariableMatching = (match) => (options) => {
  const flattenOptions = flatMap("options");
  return (
    flow(flattenOptions, find(match))(options) ||
    fallback(flattenOptions(options))
  );
};

export const findStartEndDates = (start_date, end_date) => (options) =>
  find({ value: { representative: { start_date, end_date } } })(options) ||
  fallback(options);

// Extract a value from the representative for a named option in source.
export const representativeValue = (optionName, valueName) =>
  get(compact([optionName, "value", "representative", valueName]));

// Returns an object containing the union of all representatives of the
// options named in the arguments (e.g., 'model', 'scenario').
// Returned object is suitable as a constraint for a
// `SimpleConstraintGroupingSelector`.
export const constraintsFor =
  (...optionNames) =>
  (options) =>
    flow(
      flatten,
      map((name) => options[name]),
      map((option) => option && option.value && option.value.representative),
      reduce((result, value) => assign(result, value), {}),
    )(optionNames);

// Return a filtered subset of metadata, based on the selected options
// (typically one or more of model, emissions scenario, variable).
//
// Initially, options (derived from selectors) can be undefined,
// and go through a cascading defaulting process that eventually settles
// with a defined value for all of them. Returning a metadata set that is
// filtered by a partially settled selector set causes problems in client
// components. This function returns the empty array unless a full set of
// constraints (derived from selectors) is available.
export const filterMetaBy =
  (...optionNames) =>
  (options) =>
  (meta) => {
    const settled = _.allDefined(options, ...optionNames);
    if (!settled) {
      return [];
    }
    return flow(
      filter(constraintsFor(...optionNames)(options)),
      sortBy("unique_id"),
    )(meta);
  };