Skip to content

Commit

Permalink
ui: use master grid instead of sumValuesUnderPaths for sorting
Browse files Browse the repository at this point in the history
Release note: None
  • Loading branch information
Pete Vilter committed Jul 13, 2018
1 parent ff64582 commit 86252e8
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 225 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {
TreePath,
layoutTreeHorizontal,
flatten,
sumValuesUnderPaths,
LayoutCell,
FlattenedNode,
visitNodes,
Expand Down Expand Up @@ -447,39 +446,7 @@ const selectColsWithSize = createSelector(
);

// TODO(vilterp): make this depend on the master grid

const selectFlattenedRows = createSelector(
selectRowsWithSize,
selectColsWithSize,
(propsAndState: PropsAndState) => propsAndState.state.collapsedRows,
(propsAndState: PropsAndState) => propsAndState.state.paginationStates,
selectGetValueFun,
(
rows: TreeWithSize<SchemaObject>,
cols: TreeWithSize<INodeDescriptor>,
collapsedRows: TreePath[],
paginationStates: AssocList<TreePath, PaginationState>,
getValue: (rowPath: TreePath, colPath: TreePath) => number,
) => {
console.log("flattening rows");
const sortBy = (path: TreePath) => {
// TODO(vilterp): get this from the master grid
return sumValuesUnderPaths(rows.node, cols.node, path, [], getValue);
};
return flatten(rows, collapsedRows, true /* includeNodes */, paginationStates, PAGE_SIZE, sortBy);
},
);

const selectFlattenedCols = createSelector(
selectColsWithSize,
(propsAndState: PropsAndState) => propsAndState.state.collapsedCols,
(cols: TreeWithSize<INodeDescriptor>, collapseCols: TreePath[]) => {
console.log("flattening cols");
return flatten(cols, collapseCols, false /* includeNodes */);
},
);

interface SumAndIncreate {
interface SumAndIncrease {
sum: number;
increase: number;
}
Expand All @@ -504,10 +471,10 @@ const selectMasterGrid = createSelector(
outputRows.push(row);
}

function visitRows(rowPath: TreePath, rowIdx: number, row: TreeWithSize<SchemaObject>): SumAndIncreate {
function visitRows(rowPath: TreePath, rowIdx: number, row: TreeWithSize<SchemaObject>): SumAndIncrease {
// console.log("visitRows", rowPath, rowIdx);

function visitCols(colPath: TreePath, colIdx: number, col: TreeWithSize<INodeDescriptor>): SumAndIncreate {
function visitCols(colPath: TreePath, colIdx: number, col: TreeWithSize<INodeDescriptor>): SumAndIncrease {
// console.log("visitCols", colPath, rowIdx);

if (isLeaf(col.node)) {
Expand Down Expand Up @@ -566,6 +533,34 @@ const selectMasterGrid = createSelector(
},
);

const selectFlattenedRows = createSelector(
selectMasterGrid,
selectRowsWithSize,
(propsAndState: PropsAndState) => propsAndState.state.collapsedRows,
(propsAndState: PropsAndState) => propsAndState.state.paginationStates,
(
masterGrid: number[][],
rows: TreeWithSize<SchemaObject>,
collapsedRows: TreePath[],
paginationStates: AssocList<TreePath, PaginationState>,
) => {
console.log("flattening rows");
const sortBy = (masterRowIdx: number) => {
return masterGrid[masterRowIdx][0];
};
return flatten(rows, collapsedRows, true /* includeNodes */, paginationStates, PAGE_SIZE, sortBy);
},
);

const selectFlattenedCols = createSelector(
selectColsWithSize,
(propsAndState: PropsAndState) => propsAndState.state.collapsedCols,
(cols: TreeWithSize<INodeDescriptor>, collapseCols: TreePath[]) => {
console.log("flattening cols");
return flatten(cols, collapseCols, false /* includeNodes */);
},
);

const selectAllVals = createSelector(
selectMasterGrid,
selectFlattenedRows,
Expand All @@ -585,7 +580,6 @@ const selectAllVals = createSelector(
return;
}
const value = masterGrid[row.masterIdx][col.masterIdx];
// console.log("get val from master grid:", row.path, row.masterIdx, col.path, col.masterIdx, "=>", value);
rowVals.push(value);
inSingleArray.push(value);
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { assert } from "chai";

import {
flatten, layoutTreeHorizontal, sumValuesUnderPaths, TreePath, LayoutCell,
flatten, layoutTreeHorizontal, LayoutCell,
} from "./tree";

describe("tree", () => {
Expand Down Expand Up @@ -224,56 +224,4 @@ describe("tree", () => {

});

describe("sumValuesUnderPaths", () => {

// | | C_1 |
// | | C_2 | C_3 |
// |-------|-----|-----|
// | R_a | | |
// | R_b | 1 | 2 |
// | R_c | 3 | 4 |

const rowTree = {
name: "a",
children: [
{ name: "b" },
{ name: "c" },
],
};
const colTree = {
name: "1",
children: [
{ name: "2" },
{ name: "3" },
],
};
// by row, then col.
const values: {[name: string]: {[name: string]: number}} = {
"b": {"2": 1, "3": 2},
"c": {"2": 3, "3": 4},
};
function getValue(rowPath: TreePath, colPath: TreePath): number {
return values[rowPath[0]][colPath[0]];
}

it("computes a sum for the roots of both trees", () => {
const actualSum = sumValuesUnderPaths(rowTree, colTree, [], [], getValue);
const expectedSum = 1 + 2 + 3 + 4;
assert.equal(actualSum, expectedSum);
});

it("computes a sum for the root of one tree and the leaf of another", () => {
const actualSum = sumValuesUnderPaths(rowTree, colTree, ["b"], [], getValue);
const expectedSum = 1 + 2;
assert.equal(actualSum, expectedSum);
});

it("computes a sum for a single cell (two leaves)", () => {
const actualSum = sumValuesUnderPaths(rowTree, colTree, ["b"], ["3"], getValue);
const expectedSum = 2;
assert.equal(actualSum, expectedSum);
});

});

});
161 changes: 26 additions & 135 deletions pkg/ui/src/views/cluster/containers/dataDistribution/tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ export function flatten<T>(
includeInternalNodes: boolean,
paginationStates: AssocList<TreePath, PaginationState> = [],
pageSize: number = Number.MAX_VALUE,
sortBy?: (path: TreePath) => number,
sortBy?: (masterIndex: number) => number,
): FlattenedNode<T>[] {
const output: FlattenedNode<T>[] = [];
recur(tree, [], 0);
Expand Down Expand Up @@ -355,7 +355,7 @@ export function flatten<T>(
const offset = page * pageSize;

const sortState = paginationState ? paginationState.sortState : SortState.NONE;
const sortedChildren = sortChildren(node.children, pathSoFar, sortState, sortBy);
const sortedChildren = sortChildren(node.children, sortState, sortBy);

for (let i = 0; i < offset; i++) {
const child = sortedChildren[i];
Expand All @@ -379,9 +379,8 @@ export function flatten<T>(

function sortChildren<T>(
children: TreeWithSize<T>[],
pathSoFar: TreePath,
sortState: SortState,
sortBy?: (path: TreePath) => number,
sortBy?: (masterIndex: number) => number,
): TreeWithSize<T>[] {
if (sortState === SortState.NONE) {
return children;
Expand All @@ -390,31 +389,14 @@ function sortChildren<T>(
throw Error(`sortState ${sortState} but no sortBy provided`);
}
const sortedChildren = _.sortBy(children, (child) => {
const childPath = [...pathSoFar, child.node.name];
return sortBy(childPath);
return sortBy(child.masterIdx);
});
if (sortState === SortState.DESC) {
sortedChildren.reverse();
}
return sortedChildren;
}

/**
* nodeAtPath returns the node found under `root` at `path`, throwing
* an error if nothing is found.
*/
function nodeAtPath<T>(root: TreeNode<T>, path: TreePath): TreeNode<T> {
if (path.length === 0) {
return root;
}
const pathSegment = path[0];
const child = root.children.find((c) => (c.name === pathSegment));
if (child === undefined) {
throw new Error(`not found: ${path}`);
}
return nodeAtPath(child, path.slice(1));
}

/**
* visitNodes invokes `f` on each node in the tree in pre-order
* (`f` is invoked on a node before being invoked on its children).
Expand Down Expand Up @@ -458,104 +440,6 @@ function expandedHeight<T>(root: TreeNode<T>, collapsedPaths: TreePath[]): numbe
return maxHeight;
}

/**
* getLeafPathsUnderPath returns paths to all leaf nodes under the given
* `path` in `root`.
*
* E.g. for the tree T =
*
* a/
* b/
* c
* d
* e/
* f
* g
*
* getLeafPaths(T, ['a', 'b']) yields:
*
* [ ['a', 'b', 'c'],
* ['a', 'b', 'd'] ]
*
*/
function getLeafPathsUnderPath<T>(root: TreeNode<T>, path: TreePath): TreePath[] {
const atPath = nodeAtPath(root, path);
const output: TreePath[] = [];
visitNodes(atPath, (node, subPath) => {
if (isLeaf(node)) {
output.push([...path, ...subPath]);
}
return true;
});
return output;
}

/**
* cartProd returns all combinations of elements in `as` and `bs`.
*
* e.g. cartProd([1, 2], ['a', 'b'])
* yields:
* [
* {a: 1, b: 'a'},
* {a: 1, b: 'b'},
* {a: 2, b: 'a'},
* {a: 2, b: 'b'},
* ]
*/
function cartProd<A, B>(as: A[], bs: B[]): {a: A, b: B}[] {
const output: {a: A, b: B}[] = [];
as.forEach((a) => {
bs.forEach((b) => {
output.push({ a, b });
});
});
return output;
}

/**
* sumValuesUnderPaths returns the sum of `getValue(R, C)`
* for all leaf paths R under `rowPath` in `rowTree`,
* and all leaf paths C under `colPath` in `rowTree`.
*
* E.g. in the matrix
*
* | | C_1 |
* | | C_2 | C_3 |
* |-------|-----|-----|
* | R_a | | |
* | R_b | 1 | 2 |
* | R_c | 3 | 4 |
*
* represented by
*
* rowTree = (R_a [R_b R_c])
* colTree = (C_1 [C_2 C_3])
*
* calling sumValuesUnderPath(rowTree, colTree, ['R_a'], ['C_1'], getValue)
* sums up all the cells in the matrix, yielding 1 + 2 + 3 + 4 = 10.
*
* Calling sumValuesUnderPath(rowTree, colTree, ['R_a', 'R_b'], ['C_1'], getValue)
* sums up only the cells under R_b,
* yielding 1 + 2 = 3.
*
*/
export function sumValuesUnderPaths<R, C>(
rowTree: TreeNode<R>,
colTree: TreeNode<C>,
rowPath: TreePath,
colPath: TreePath,
getValue: (row: TreePath, col: TreePath) => number,
): number {
const rowPaths = getLeafPathsUnderPath(rowTree, rowPath);
const colPaths = getLeafPathsUnderPath(colTree, colPath);
const prod = cartProd(rowPaths, colPaths);
let sum = 0;
prod.forEach((coords) => {
sum += getValue(coords.a, coords.b);
});
return sum;
}

/**
* deepIncludes returns true if `array` contains `val`, doing
* a deep equality comparison.
Expand All @@ -578,30 +462,37 @@ export function repeat<T>(times: number, item: T): T[] {

export interface TreeWithSize<T> {
size: number;
masterIdx: number;
node: TreeNode<T>;
children?: TreeWithSize<T>[];
}

// TODO(vilterp): not store the child arrays twice...
// maybe actually just add teh size to teh same struct
export function augmentWithSize<T>(node: TreeNode<T>): TreeWithSize<T> {
if (isLeaf(node)) {
export function augmentWithSize<T>(root: TreeNode<T>): TreeWithSize<T> {
function recur(node: TreeNode<T>, masterIdx: number): TreeWithSize<T> {
if (isLeaf(node)) {
return {
size: 1,
masterIdx,
node,
};
}

let size = 0;
const children: TreeWithSize<T>[] = [];
node.children.forEach((child) => {
const augmentedChild = recur(child, masterIdx + size);
size += augmentedChild.size;
children.push(augmentedChild);
});
return {
size: 1,
size: size + 1, // add 1 for the node itself
children,
node,
masterIdx,
};
}

let size = 1; // node itself
const children: TreeWithSize<T>[] = [];
node.children.forEach((child) => {
const augmentedChild = augmentWithSize(child);
size += augmentedChild.size;
children.push(augmentedChild);
});
return {
size,
children,
node,
};
return recur(root, 0);
}

0 comments on commit 86252e8

Please sign in to comment.