mirror of
https://github.com/chanzuckerberg/cellxgene.git
synced 2026-10-04 07:38:12 +08:00
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:
committed by
Colin Megill
parent
c12cb2424a
commit
846b8d15bd
@@ -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])
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import {
|
import {
|
||||||
fillRange,
|
|
||||||
sliceByIndex,
|
sliceByIndex,
|
||||||
makeSortIndex
|
makeSortIndex
|
||||||
} from "../../../src/util/typedCrossfilter/util";
|
} from "../../../src/util/typedCrossfilter/util";
|
||||||
|
import { rangeFill as fillRange } from "../../../src/util/range";
|
||||||
|
|
||||||
describe("fillRange", () => {
|
describe("fillRange", () => {
|
||||||
test("Array", () => {
|
test("Array", () => {
|
||||||
|
|||||||
+3
-1
@@ -142,7 +142,9 @@
|
|||||||
],
|
],
|
||||||
"@babel/plugin-proposal-export-namespace-from",
|
"@babel/plugin-proposal-export-namespace-from",
|
||||||
"@babel/plugin-transform-react-constant-elements",
|
"@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"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
// jshint esversion: 6
|
// jshint esversion: 6
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import _ from "lodash";
|
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import * as d3 from "d3";
|
import * as d3 from "d3";
|
||||||
|
|
||||||
@@ -17,9 +16,7 @@ class Occupancy extends React.Component {
|
|||||||
const width = 100;
|
const width = 100;
|
||||||
const height = 11;
|
const height = 11;
|
||||||
|
|
||||||
const categories = _.filter(schema.annotations.obs, {
|
const categories = schema.annotations.obsByName[colorAccessor]?.categories;
|
||||||
name: colorAccessor
|
|
||||||
})[0].categories;
|
|
||||||
|
|
||||||
const x = d3
|
const x = d3
|
||||||
.scaleLinear()
|
.scaleLinear()
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
// return sorted index
|
// return sorted index
|
||||||
|
|
||||||
import isNumber from "is-number";
|
import isNumber from "is-number";
|
||||||
import _ from "lodash";
|
|
||||||
|
|
||||||
const sortedCategoryValues = values => {
|
const sortedCategoryValues = values => {
|
||||||
/* this sort could be memoized for perf */
|
/* this sort could be memoized for perf */
|
||||||
@@ -13,7 +12,7 @@ const sortedCategoryValues = values => {
|
|||||||
const strings = [];
|
const strings = [];
|
||||||
const ints = [];
|
const ints = [];
|
||||||
|
|
||||||
_.forEach(values, v => {
|
values.forEach(v => {
|
||||||
if (isNumber(v[0])) {
|
if (isNumber(v[0])) {
|
||||||
ints.push(v);
|
ints.push(v);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
// jshint esversion: 6
|
// jshint esversion: 6
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import _ from "lodash";
|
|
||||||
import Occupancy from "./occupancy";
|
import Occupancy from "./occupancy";
|
||||||
import { countCategoryValues2D } from "../../util/stateManager/worldUtil";
|
import { countCategoryValues2D } from "../../util/stateManager/worldUtil";
|
||||||
import * as globals from "../../globals";
|
import * as globals from "../../globals";
|
||||||
@@ -10,7 +9,7 @@ import * as globals from "../../globals";
|
|||||||
categoricalSelection: state.categoricalSelection,
|
categoricalSelection: state.categoricalSelection,
|
||||||
colorScale: state.colors.scale,
|
colorScale: state.colors.scale,
|
||||||
colorAccessor: state.colors.colorAccessor,
|
colorAccessor: state.colors.colorAccessor,
|
||||||
schema: _.get(state.world, "schema", null),
|
schema: state.world?.schema,
|
||||||
world: state.world
|
world: state.world
|
||||||
}))
|
}))
|
||||||
class CategoryValue extends React.Component {
|
class CategoryValue extends React.Component {
|
||||||
@@ -60,9 +59,7 @@ class CategoryValue extends React.Component {
|
|||||||
let occupancy = null;
|
let occupancy = null;
|
||||||
|
|
||||||
if (isColorBy && schema) {
|
if (isColorBy && schema) {
|
||||||
categories = _.filter(schema.annotations.obs, {
|
categories = schema.annotations.obsByName[colorAccessor]?.categories;
|
||||||
name: colorAccessor
|
|
||||||
})[0].categories;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (colorAccessor && !isColorBy && categoricalSelection[colorAccessor]) {
|
if (colorAccessor && !isColorBy && categoricalSelection[colorAccessor]) {
|
||||||
|
|||||||
@@ -9,10 +9,10 @@ import * as globals from "../../globals";
|
|||||||
import HistogramBrush from "../brushableHistogram";
|
import HistogramBrush from "../brushableHistogram";
|
||||||
|
|
||||||
@connect(state => ({
|
@connect(state => ({
|
||||||
obsAnnotations: _.get(state.world, "obsAnnotations", null),
|
obsAnnotations: state.world?.obsAnnotations,
|
||||||
colorAccessor: state.colors.colorAccessor,
|
colorAccessor: state.colors.colorAccessor,
|
||||||
colorScale: state.colors.scale,
|
colorScale: state.colors.scale,
|
||||||
schema: _.get(state.world, "schema", null)
|
schema: state.world?.schema
|
||||||
}))
|
}))
|
||||||
class Continuous extends React.Component {
|
class Continuous extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ const filterGenes = (query, genes) =>
|
|||||||
|
|
||||||
@connect(state => {
|
@connect(state => {
|
||||||
return {
|
return {
|
||||||
obsAnnotations: _.get(state.world, "obsAnnotations", null),
|
obsAnnotations: state.world?.obsAnnotations,
|
||||||
userDefinedGenes: state.controls.userDefinedGenes,
|
userDefinedGenes: state.controls.userDefinedGenes,
|
||||||
userDefinedGenesLoading: state.controls.userDefinedGenesLoading,
|
userDefinedGenesLoading: state.controls.userDefinedGenesLoading,
|
||||||
world: state.world,
|
world: state.world,
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
// jshint esversion: 6
|
// jshint esversion: 6
|
||||||
import _ from "lodash";
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import Categorical from "./categorical/categorical";
|
import Categorical from "./categorical/categorical";
|
||||||
@@ -10,7 +9,7 @@ import DynamicScatterplot from "./scatterplot/scatterplot";
|
|||||||
|
|
||||||
@connect(state => ({
|
@connect(state => ({
|
||||||
responsive: state.responsive,
|
responsive: state.responsive,
|
||||||
datasetTitle: _.get(state.config, "displayNames.dataset"),
|
datasetTitle: state.config?.displayNames?.dataset,
|
||||||
scatterplotXXaccessor: state.controls.scatterplotXXaccessor,
|
scatterplotXXaccessor: state.controls.scatterplotXXaccessor,
|
||||||
scatterplotYYaccessor: state.controls.scatterplotYYaccessor
|
scatterplotYYaccessor: state.controls.scatterplotYYaccessor
|
||||||
}))
|
}))
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
import _ from "lodash";
|
|
||||||
|
|
||||||
import { ControlsHelpers } from "../util/stateManager";
|
import { ControlsHelpers } from "../util/stateManager";
|
||||||
import * as globals from "../globals";
|
import * as globals from "../globals";
|
||||||
|
|
||||||
function maxCategoryItems(state) {
|
function maxCategoryItems(state) {
|
||||||
return _.get(
|
return (
|
||||||
state.config,
|
state.config.parameters?.["max-category-items"] ??
|
||||||
"parameters.max-category-items",
|
|
||||||
globals.configDefaults.parameters["max-category-items"]
|
globals.configDefaults.parameters["max-category-items"]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ Label indexing - map a label to & from an integer offset. See Dataframe
|
|||||||
for how this is used.
|
for how this is used.
|
||||||
**/
|
**/
|
||||||
|
|
||||||
|
import { rangeFill as fillRange } from "../range";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Private utility functions
|
Private utility functions
|
||||||
*/
|
*/
|
||||||
@@ -21,14 +23,6 @@ function extent(tarr) {
|
|||||||
return [min, max];
|
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 */
|
/* eslint-disable class-methods-use-this */
|
||||||
class IdentityInt32Index {
|
class IdentityInt32Index {
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
/*
|
/*
|
||||||
Helper functions for the embedded graph colors
|
Helper functions for the embedded graph colors
|
||||||
*/
|
*/
|
||||||
import _ from "lodash";
|
|
||||||
import * as d3 from "d3";
|
import * as d3 from "d3";
|
||||||
import { interpolateRainbow, interpolateCool } from "d3-scale-chromatic";
|
import { interpolateRainbow, interpolateCool } from "d3-scale-chromatic";
|
||||||
import * as globals from "../../globals";
|
import * as globals from "../../globals";
|
||||||
import parseRGB from "../parseRGB";
|
import parseRGB from "../parseRGB";
|
||||||
import finiteExtent from "../finiteExtent";
|
import finiteExtent from "../finiteExtent";
|
||||||
|
import { range } from "../range";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
create new colors state object. Paramters:
|
create new colors state object. Paramters:
|
||||||
@@ -37,9 +37,7 @@ function createColors(world, colorMode = null, colorAccessor = null) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function createColorsByCategoricalMetadata(world, accessor) {
|
function createColorsByCategoricalMetadata(world, accessor) {
|
||||||
const { categories } = _.filter(world.schema.annotations.obs, {
|
const { categories } = world.schema.annotations.obsByName[accessor];
|
||||||
name: accessor
|
|
||||||
})[0];
|
|
||||||
|
|
||||||
const scale = d3
|
const scale = d3
|
||||||
.scaleSequential(interpolateRainbow)
|
.scaleSequential(interpolateRainbow)
|
||||||
@@ -67,7 +65,7 @@ function createColorsByContinuousMetadata(world, accessor) {
|
|||||||
const scale = d3
|
const scale = d3
|
||||||
.scaleQuantile()
|
.scaleQuantile()
|
||||||
.domain([min, max])
|
.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 */
|
/* pre-create colors - much faster than doing it for each obs */
|
||||||
const colors = new Array(colorBins);
|
const colors = new Array(colorBins);
|
||||||
@@ -97,7 +95,7 @@ function createColorsByExpression(world, accessor) {
|
|||||||
const scale = d3
|
const scale = d3
|
||||||
.scaleQuantile()
|
.scaleQuantile()
|
||||||
.domain([min, max])
|
.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 */
|
/* pre-create colors - much faster than doing it for each obs */
|
||||||
const colors = new Array(colorBins);
|
const colors = new Array(colorBins);
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ Helper functions for the controls reducer
|
|||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
|
|
||||||
import * as globals from "../../globals";
|
import * as globals from "../../globals";
|
||||||
import { fillRange } from "../typedCrossfilter/util";
|
import { rangeFill as fillRange } from "../range";
|
||||||
import {
|
import {
|
||||||
userDefinedDimensionName,
|
userDefinedDimensionName,
|
||||||
diffexpDimensionName
|
diffexpDimensionName
|
||||||
|
|||||||
@@ -122,15 +122,15 @@ function reconcileSchemaCategoriesWithSummary(universe) {
|
|||||||
cases, add a 'categories' field to the schema so it is accessible.
|
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 (
|
if (
|
||||||
s.type === "string" ||
|
s.type === "string" ||
|
||||||
s.type === "boolean" ||
|
s.type === "boolean" ||
|
||||||
s.type === "categorical"
|
s.type === "categorical"
|
||||||
) {
|
) {
|
||||||
const categories = _.union(
|
const categories = _.union(
|
||||||
_.get(s, "categories", []),
|
s.categories ?? [],
|
||||||
_.get(universe.obsAnnotations.col(s.name).summarize(), "categories", [])
|
universe.obsAnnotations.col(s.name).summarize().categories ?? []
|
||||||
);
|
);
|
||||||
s.categories = categories;
|
s.categories = categories;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,12 @@
|
|||||||
// jshint esversion: 6
|
// jshint esversion: 6
|
||||||
|
|
||||||
import { sortIndex } from "./sort";
|
import { sortIndex } from "./sort";
|
||||||
|
import { rangeFill as fillRange } from "../range";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Utility functions, private to this module.
|
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
|
// slice out of one array into another, using an index array
|
||||||
//
|
//
|
||||||
export function sliceByIndex(src, index) {
|
export function sliceByIndex(src, index) {
|
||||||
|
|||||||
Reference in New Issue
Block a user