lodash cleanup (#747)

* add own range() function

* lodash cleanup

* remove redundant fill range implementations

* remove use of _.get

* sync test babel config with build

* update tests to match new range implementation
This commit is contained in:
Bruce Martin
2019-05-06 17:28:32 -07:00
committed by Colin Megill
parent c12cb2424a
commit 846b8d15bd
16 changed files with 112 additions and 52 deletions

View File

@@ -0,0 +1,42 @@
import { range, rangeFill } from "../../src/util/range";
describe("range", () => {
test("no defaults", () => {
expect(range(0, 3, 1)).toMatchObject([0, 1, 2]);
});
test("range(stop)", () => {
expect(range(3)).toMatchObject([0, 1, 2]);
expect(range(0)).toMatchObject([]);
expect(range(1)).toMatchObject([0]);
});
test("range(start,stop)", () => {
expect(range(0, 0)).toMatchObject([]);
expect(range(0, 2)).toMatchObject([0, 1]);
expect(range(4, 8)).toMatchObject([4, 5, 6, 7]);
});
test("range(start, stop, step", () => {
expect(range(4, 0, -1)).toMatchObject([4, 3, 2, 1]);
expect(range(0, 4, 2)).toMatchObject([0, 2]);
});
});
describe("rangefill", () => {
test("rangeFill(arr)", () => {
expect(rangeFill(new Int32Array(3))).toMatchObject(
new Int32Array([0, 1, 2])
);
});
test("rangeFill(arr, start)", () => {
expect(rangeFill(new Int32Array(2), 1)).toMatchObject(
new Int32Array([1, 2])
);
});
test("rangeFill(arr, start, step)", () => {
expect(rangeFill(new Int32Array(3), 2, -1)).toMatchObject(
new Int32Array([2, 1, 0])
);
});
});

View File

@@ -1,8 +1,8 @@
import {
fillRange,
sliceByIndex,
makeSortIndex
} from "../../../src/util/typedCrossfilter/util";
import { rangeFill as fillRange } from "../../../src/util/range";
describe("fillRange", () => {
test("Array", () => {

View File

@@ -142,7 +142,9 @@
],
"@babel/plugin-proposal-export-namespace-from",
"@babel/plugin-transform-react-constant-elements",
"@babel/plugin-transform-runtime"
"@babel/plugin-transform-runtime",
"@babel/plugin-proposal-optional-chaining",
"@babel/plugin-proposal-nullish-coalescing-operator"
]
}
}

View File

@@ -1,6 +1,5 @@
// jshint esversion: 6
import React from "react";
import _ from "lodash";
import { connect } from "react-redux";
import * as d3 from "d3";
@@ -17,9 +16,7 @@ class Occupancy extends React.Component {
const width = 100;
const height = 11;
const categories = _.filter(schema.annotations.obs, {
name: colorAccessor
})[0].categories;
const categories = schema.annotations.obsByName[colorAccessor]?.categories;
const x = d3
.scaleLinear()

View File

@@ -5,7 +5,6 @@
// return sorted index
import isNumber from "is-number";
import _ from "lodash";
const sortedCategoryValues = values => {
/* this sort could be memoized for perf */
@@ -13,7 +12,7 @@ const sortedCategoryValues = values => {
const strings = [];
const ints = [];
_.forEach(values, v => {
values.forEach(v => {
if (isNumber(v[0])) {
ints.push(v);
} else {

View File

@@ -1,7 +1,6 @@
// jshint esversion: 6
import { connect } from "react-redux";
import React from "react";
import _ from "lodash";
import Occupancy from "./occupancy";
import { countCategoryValues2D } from "../../util/stateManager/worldUtil";
import * as globals from "../../globals";
@@ -10,7 +9,7 @@ import * as globals from "../../globals";
categoricalSelection: state.categoricalSelection,
colorScale: state.colors.scale,
colorAccessor: state.colors.colorAccessor,
schema: _.get(state.world, "schema", null),
schema: state.world?.schema,
world: state.world
}))
class CategoryValue extends React.Component {
@@ -60,9 +59,7 @@ class CategoryValue extends React.Component {
let occupancy = null;
if (isColorBy && schema) {
categories = _.filter(schema.annotations.obs, {
name: colorAccessor
})[0].categories;
categories = schema.annotations.obsByName[colorAccessor]?.categories;
}
if (colorAccessor && !isColorBy && categoricalSelection[colorAccessor]) {

View File

@@ -9,10 +9,10 @@ import * as globals from "../../globals";
import HistogramBrush from "../brushableHistogram";
@connect(state => ({
obsAnnotations: _.get(state.world, "obsAnnotations", null),
obsAnnotations: state.world?.obsAnnotations,
colorAccessor: state.colors.colorAccessor,
colorScale: state.colors.scale,
schema: _.get(state.world, "schema", null)
schema: state.world?.schema
}))
class Continuous extends React.Component {
constructor(props) {

View File

@@ -57,7 +57,7 @@ const filterGenes = (query, genes) =>
@connect(state => {
return {
obsAnnotations: _.get(state.world, "obsAnnotations", null),
obsAnnotations: state.world?.obsAnnotations,
userDefinedGenes: state.controls.userDefinedGenes,
userDefinedGenesLoading: state.controls.userDefinedGenesLoading,
world: state.world,

View File

@@ -1,5 +1,4 @@
// jshint esversion: 6
import _ from "lodash";
import React from "react";
import { connect } from "react-redux";
import Categorical from "./categorical/categorical";
@@ -10,7 +9,7 @@ import DynamicScatterplot from "./scatterplot/scatterplot";
@connect(state => ({
responsive: state.responsive,
datasetTitle: _.get(state.config, "displayNames.dataset"),
datasetTitle: state.config?.displayNames?.dataset,
scatterplotXXaccessor: state.controls.scatterplotXXaccessor,
scatterplotYYaccessor: state.controls.scatterplotYYaccessor
}))

View File

@@ -1,12 +1,9 @@
import _ from "lodash";
import { ControlsHelpers } from "../util/stateManager";
import * as globals from "../globals";
function maxCategoryItems(state) {
return _.get(
state.config,
"parameters.max-category-items",
return (
state.config.parameters?.["max-category-items"] ??
globals.configDefaults.parameters["max-category-items"]
);
}

View File

@@ -3,6 +3,8 @@ Label indexing - map a label to & from an integer offset. See Dataframe
for how this is used.
**/
import { rangeFill as fillRange } from "../range";
/*
Private utility functions
*/
@@ -21,14 +23,6 @@ function extent(tarr) {
return [min, max];
}
function fillRange(arr, start = 0) {
const larr = arr;
for (let i = 0, l = larr.length; i < l; i += 1) {
larr[i] = i + start;
}
return larr;
}
/* eslint-disable class-methods-use-this */
class IdentityInt32Index {
/*

45
client/src/util/range.js Normal file
View File

@@ -0,0 +1,45 @@
/*
Array range creation
range(start, stop, step) -> Array
This is identical to https://docs.python.org/3/library/functions.html#func-range
Returns new array filled with a range of numbers.
Usage:
range(stop) - start defaults to zero, step defaults to 1
range(start, stop, [step]) - step defaults to 1
Examples:
range(3) -> [0, 1, 2]
range(1, 3) -> [1, 2]
range(1, 5, 2) -> [1, 3]
rangeFill(array, start, step) -> array
Fill entire array with values, from start, by step. Returns first array.
start defaults to zero, step defaults to one.
*/
function _doFill(arr, start, step, count) {
for (let idx = 0, val = start; idx < count; idx += 1, val += step) {
arr[idx] = val;
}
return arr;
}
export function rangeFill(arr, start = 0, step = 1) {
return _doFill(arr, start, step, arr.length);
}
export function range(start, stop, step) {
if (start === undefined) return [];
if (stop === undefined) {
stop = start;
start = 0;
}
step = step || 1; // catch undefind and zero
const len = Math.max(Math.ceil((stop - start) / step), 0);
return _doFill(new Array(len), start, step, len);
}

View File

@@ -1,12 +1,12 @@
/*
Helper functions for the embedded graph colors
*/
import _ from "lodash";
import * as d3 from "d3";
import { interpolateRainbow, interpolateCool } from "d3-scale-chromatic";
import * as globals from "../../globals";
import parseRGB from "../parseRGB";
import finiteExtent from "../finiteExtent";
import { range } from "../range";
/*
create new colors state object. Paramters:
@@ -37,9 +37,7 @@ function createColors(world, colorMode = null, colorAccessor = null) {
}
function createColorsByCategoricalMetadata(world, accessor) {
const { categories } = _.filter(world.schema.annotations.obs, {
name: accessor
})[0];
const { categories } = world.schema.annotations.obsByName[accessor];
const scale = d3
.scaleSequential(interpolateRainbow)
@@ -67,7 +65,7 @@ function createColorsByContinuousMetadata(world, accessor) {
const scale = d3
.scaleQuantile()
.domain([min, max])
.range(_.range(colorBins - 1, -1, -1));
.range(range(colorBins - 1, -1, -1));
/* pre-create colors - much faster than doing it for each obs */
const colors = new Array(colorBins);
@@ -97,7 +95,7 @@ function createColorsByExpression(world, accessor) {
const scale = d3
.scaleQuantile()
.domain([min, max])
.range(_.range(colorBins - 1, -1, -1));
.range(range(colorBins - 1, -1, -1));
/* pre-create colors - much faster than doing it for each obs */
const colors = new Array(colorBins);

View File

@@ -5,7 +5,7 @@ Helper functions for the controls reducer
import _ from "lodash";
import * as globals from "../../globals";
import { fillRange } from "../typedCrossfilter/util";
import { rangeFill as fillRange } from "../range";
import {
userDefinedDimensionName,
diffexpDimensionName

View File

@@ -122,15 +122,15 @@ function reconcileSchemaCategoriesWithSummary(universe) {
cases, add a 'categories' field to the schema so it is accessible.
*/
_.forEach(universe.schema.annotations.obs, s => {
universe.schema.annotations.obs.forEach(s => {
if (
s.type === "string" ||
s.type === "boolean" ||
s.type === "categorical"
) {
const categories = _.union(
_.get(s, "categories", []),
_.get(universe.obsAnnotations.col(s.name).summarize(), "categories", [])
s.categories ?? [],
universe.obsAnnotations.col(s.name).summarize().categories ?? []
);
s.categories = categories;
}

View File

@@ -1,22 +1,12 @@
// jshint esversion: 6
import { sortIndex } from "./sort";
import { rangeFill as fillRange } from "../range";
/*
Utility functions, private to this module.
*/
// fill an array or typedarray with a sequential range of numbers,
// starting with `start`
//
export function fillRange(arr, start = 0) {
const larr = arr;
for (let i = 0, len = larr.length; i < len; i += 1) {
larr[i] = i + start;
}
return larr;
}
// slice out of one array into another, using an index array
//
export function sliceByIndex(src, index) {