<commit_before>import React from "react";
import { connect } from "react-redux";
import "react-select/dist/react-select.css";
import "react-virtualized-select/styles.css";
import Select from "react-virtualized-select";
import { debounce } from 'lodash';
import { controlsWidth, isValueValid, strainSymbol, genotypeSymbol} from "../../util/globals";
import { collectGenotypeStates } from "../../util/treeMiscHelpers";
import { applyFilter } from "../../actions/tree";
import { FilterBadge } from "../info/filterBadge";
import { SidebarSubtitle } from "./styles";

const DEBOUNCE_TIME = 200;

/**
 * <FilterData> is a (keyboard)-typing based search box intended to
 * allow users to filter samples. The filtering rules are not implemented
 * in this component, but are useful to spell out: we take the union of
 * entries within each category and then take the intersection of those unions.
 */
@connect((state) => {
  return {
    activeFilters: state.controls.filters,
    totalStateCounts: state.tree.totalStateCounts,
    nodes: state.tree.nodes
  };
})
class FilterData extends React.Component {
  constructor(props) {
    super(props);
  }

  getStyles() {
    return {
      base: {
        width: controlsWidth,
        marginBottom: 0,
        fontSize: 14
      }
    };
  }
  makeOptions = () => {
    /**
     * The <Select> component needs an array of options to display (and search across). We compute this
     * by looping across each filter and calculating all valid options for each. This function runs
     * each time a filter is toggled on / off.
     */
    const options = [];
    Object.keys(this.props.activeFilters)
      .forEach((filterName) => {
        const filterValuesCurrentlyActive = this.props.activeFilters[filterName].filter((x) => x.active).map((x) => x.value);
        Array.from(this.props.totalStateCounts[filterName].keys())
          .filter((itemName) => isValueValid(itemName)) // remove invalid values present across the tree
          .filter((itemName) => !filterValuesCurrentlyActive.includes(itemName)) // remove already enabled filters
          .sort() // filters are sorted alphabetically - probably not necessary for a select component
          .forEach((itemName) => {
            options.push({
              label: `${filterName} → ${itemName}`,
              value: [filterName, itemName]
            });
          });
      });
    if (genotypeSymbol in this.props.activeFilters) {
      const sortedGenotypes = [...collectGenotypeStates(this.props.nodes)].sort();
      options.push(...sortedGenotypes.map((o) => ({
        label: `genotype ${o}`,
        value: [genotypeSymbol, o]
      })));
    }
    if (strainSymbol in this.props.activeFilters) {
      this.props.nodes
        .filter((n) => !n.hasChildren)
        .forEach((n) => {
          options.push({
            label: `sample → ${n.name}`,
            value: [strainSymbol, n.name]
          });
        });
    }
    return options;<commit_msg>controls/filter: Avoid spread syntax with potentially large arrays<commit_after>import React from "react";
import { connect } from "react-redux";
import "react-select/dist/react-select.css";
import "react-virtualized-select/styles.css";
import Select from "react-virtualized-select";
import { debounce } from 'lodash';
import { controlsWidth, isValueValid, strainSymbol, genotypeSymbol} from "../../util/globals";
import { collectGenotypeStates } from "../../util/treeMiscHelpers";
import { applyFilter } from "../../actions/tree";
import { FilterBadge } from "../info/filterBadge";
import { SidebarSubtitle } from "./styles";

const DEBOUNCE_TIME = 200;

/**
 * <FilterData> is a (keyboard)-typing based search box intended to
 * allow users to filter samples. The filtering rules are not implemented
 * in this component, but are useful to spell out: we take the union of
 * entries within each category and then take the intersection of those unions.
 */
@connect((state) => {
  return {
    activeFilters: state.controls.filters,
    totalStateCounts: state.tree.totalStateCounts,
    nodes: state.tree.nodes
  };
})
class FilterData extends React.Component {
  constructor(props) {
    super(props);
  }

  getStyles() {
    return {
      base: {
        width: controlsWidth,
        marginBottom: 0,
        fontSize: 14
      }
    };
  }
  makeOptions = () => {
    /**
     * The <Select> component needs an array of options to display (and search across). We compute this
     * by looping across each filter and calculating all valid options for each. This function runs
     * each time a filter is toggled on / off.
     */
    const options = [];
    Object.keys(this.props.activeFilters)
      .forEach((filterName) => {
        const filterValuesCurrentlyActive = this.props.activeFilters[filterName].filter((x) => x.active).map((x) => x.value);
        Array.from(this.props.totalStateCounts[filterName].keys())
          .filter((itemName) => isValueValid(itemName)) // remove invalid values present across the tree
          .filter((itemName) => !filterValuesCurrentlyActive.includes(itemName)) // remove already enabled filters
          .sort() // filters are sorted alphabetically - probably not necessary for a select component
          .forEach((itemName) => {
            options.push({
              label: `${filterName} → ${itemName}`,
              value: [filterName, itemName]
            });
          });
      });
    if (genotypeSymbol in this.props.activeFilters) {
      Array.from(collectGenotypeStates(this.props.nodes))
        .sort()
        .forEach((o) => {
          options.push({
            label: `genotype ${o}`,
            value: [genotypeSymbol, o]
          });
        });
    }
    if (strainSymbol in this.props.activeFilters) {
      this.props.nodes
        .filter((n) => !n.hasChildren)
        .forEach((n) => {
          options.push({
            label: `sample → ${n.name}`,
            value: [strainSymbol, n.name]
          });
        });
    }
    return options;
{'fraction_non_alphanumeric': 0.1291396238974871, 'fraction_numerical': 0.00199700449326011, 'mean_word_length': 2.719059405940594, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 29, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '226818', 'n_tokens_mistral': 1668, 'n_tokens_neox': 1656, 'n_words': 472}