Change WeightedTypes to contain maps (#795)

This will make it easier to re-organize the weight components so that
the WeightedTypes have a single source of truth, as described in
https://github.com/sourcecred/sourcecred/pull/792#issuecomment-419234721

Test plan: Unit tests suffice.
This commit is contained in:
Dandelion Mané 2018-09-06 16:58:40 -07:00 committed by GitHub
parent ad5ea761ea
commit ead0157960
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 106 additions and 80 deletions

View File

@ -1,7 +1,7 @@
// @flow
import React from "react";
import deepEqual from "lodash.isequal";
import * as MapUtil from "../../../util/map";
import {NodeTypeConfig} from "./NodeTypeConfig";
import {EdgeTypeConfig} from "./EdgeTypeConfig";
import {StaticPluginAdapter} from "../../adapters/pluginAdapter";
@ -37,15 +37,18 @@ export class PluginWeightConfig extends React.Component<Props, State> {
}
_renderNodeWeightControls() {
return this.state.weights.nodes.map((wnt: WeightedNodeType) => {
return Array.from(this.state.weights.nodes.values()).map(
(wnt: WeightedNodeType) => {
const onChange = (newType: WeightedNodeType) => {
const index = this.state.weights.nodes.findIndex((x) =>
deepEqual(x.type, wnt.type)
this.setState(
(state) => {
const newNodes = MapUtil.copy(state.weights.nodes);
newNodes.set(newType.type.prefix, newType);
const weights = {...state.weights, nodes: newNodes};
return {weights};
},
() => this.fire()
);
const newNodes = this.state.weights.nodes.slice();
newNodes[index] = newType;
const weights = {nodes: newNodes, edges: this.state.weights.edges};
this.setState({weights}, () => this.fire());
};
return (
<NodeTypeConfig
@ -54,19 +57,23 @@ export class PluginWeightConfig extends React.Component<Props, State> {
onChange={onChange}
/>
);
});
}
);
}
_renderEdgeWeightControls() {
return this.state.weights.edges.map((wnt: WeightedEdgeType) => {
return Array.from(this.state.weights.edges.values()).map(
(wnt: WeightedEdgeType) => {
const onChange = (newType: WeightedEdgeType) => {
const index = this.state.weights.edges.findIndex((x) =>
deepEqual(x.type, wnt.type)
this.setState(
(state) => {
const newEdges = MapUtil.copy(state.weights.edges);
newEdges.set(newType.type.prefix, newType);
const weights = {...state.weights, edges: newEdges};
return {weights};
},
() => this.fire()
);
const newEdges = this.state.weights.edges.slice();
newEdges[index] = newType;
const weights = {nodes: this.state.weights.nodes, edges: newEdges};
this.setState({weights}, () => this.fire());
};
return (
<EdgeTypeConfig
@ -75,7 +82,8 @@ export class PluginWeightConfig extends React.Component<Props, State> {
onChange={onChange}
/>
);
});
}
);
}
render() {

View File

@ -55,8 +55,10 @@ describe("src/app/credExplorer/weights/PluginWeightConfig", () => {
const newWeightedType = {...nodes[0], weight: 707};
const newNodes = [newWeightedType, ...nodes.slice(1)];
const expected = {
nodes: newNodes,
edges: adapter.edgeTypes().map(defaultWeightedEdgeType),
nodes: new Map(newNodes.map((x) => [x.type.prefix, x])),
edges: new Map(
adapter.edgeTypes().map((x) => [x.prefix, defaultWeightedEdgeType(x)])
),
};
ntc.props().onChange(newWeightedType);
expect(onChange).toHaveBeenCalledTimes(2);
@ -69,8 +71,10 @@ describe("src/app/credExplorer/weights/PluginWeightConfig", () => {
const newWeightedType = {...edges[0], weight: 707};
const newEdges = [newWeightedType, ...edges.slice(1)];
const expected = {
nodes: adapter.nodeTypes().map(defaultWeightedNodeType),
edges: newEdges,
nodes: new Map(
adapter.nodeTypes().map((x) => [x.prefix, defaultWeightedNodeType(x)])
),
edges: new Map(newEdges.map((x) => [x.type.prefix, x])),
};
ntc.props().onChange(newWeightedType);
expect(onChange).toHaveBeenCalledTimes(2);

View File

@ -1,6 +1,7 @@
// @flow
import {NodeAddress, EdgeAddress} from "../../../core/graph";
import * as MapUtil from "../../../util/map";
import {type NodeAddressT, type EdgeAddressT} from "../../../core/graph";
import type {NodeType, EdgeType} from "../../adapters/pluginAdapter";
import type {StaticPluginAdapter} from "../../adapters/pluginAdapter";
import type {StaticAdapterSet} from "../../adapters/adapterSet";
@ -14,8 +15,9 @@ export type WeightedEdgeType = {|
|};
export type WeightedTypes = {|
+nodes: $ReadOnlyArray<WeightedNodeType>,
+edges: $ReadOnlyArray<WeightedEdgeType>,
// Map from the weighted type's prefix to the type
+nodes: Map<NodeAddressT, WeightedNodeType>,
+edges: Map<EdgeAddressT, WeightedEdgeType>,
|};
export function defaultWeightedNodeType(type: NodeType): WeightedNodeType {
@ -37,34 +39,22 @@ export function defaultWeightsForAdapter(
adapter: StaticPluginAdapter
): WeightedTypes {
return {
nodes: adapter.nodeTypes().map(defaultWeightedNodeType),
edges: adapter.edgeTypes().map(defaultWeightedEdgeType),
nodes: new Map(
adapter.nodeTypes().map((x) => [x.prefix, defaultWeightedNodeType(x)])
),
edges: new Map(
adapter.edgeTypes().map((x) => [x.prefix, defaultWeightedEdgeType(x)])
),
};
}
export function combineWeights(
ws: $ReadOnlyArray<WeightedTypes>
): WeightedTypes {
const seenPrefixes = new Set();
const nodes = [].concat(...ws.map((x) => x.nodes));
for (const {
type: {prefix},
} of nodes) {
if (seenPrefixes.has(prefix)) {
throw new Error(`Duplicate prefix: ${NodeAddress.toString(prefix)}`);
}
seenPrefixes.add(prefix);
}
const edges = [].concat(...ws.map((x) => x.edges));
for (const {
type: {prefix},
} of edges) {
if (seenPrefixes.has(prefix)) {
throw new Error(`Duplicate prefix: ${EdgeAddress.toString(prefix)}`);
}
seenPrefixes.add(prefix);
}
return {nodes, edges};
return {
nodes: MapUtil.merge(ws.map((x) => x.nodes)),
edges: MapUtil.merge(ws.map((x) => x.edges)),
};
}
export function defaultWeightsForAdapterSet(

View File

@ -34,8 +34,12 @@ describe("app/credExplorer/weights/weights", () => {
it("works on the demo adapter", () => {
const adapter = new FactorioStaticAdapter();
const expected = {
nodes: adapter.nodeTypes().map(defaultWeightedNodeType),
edges: adapter.edgeTypes().map(defaultWeightedEdgeType),
nodes: new Map(
adapter.nodeTypes().map((x) => [x.prefix, defaultWeightedNodeType(x)])
),
edges: new Map(
adapter.edgeTypes().map((x) => [x.prefix, defaultWeightedEdgeType(x)])
),
};
expect(defaultWeightsForAdapter(adapter)).toEqual(expected);
});
@ -43,15 +47,27 @@ describe("app/credExplorer/weights/weights", () => {
describe("combineWeights", () => {
const defaultWeights = () =>
defaultWeightsForAdapter(new FactorioStaticAdapter());
const emptyWeights = () => ({nodes: [], edges: []});
const emptyWeights = () => ({nodes: new Map(), edges: new Map()});
it("successfully combines WeightedTypes", () => {
const weights1 = {
nodes: [defaultWeightedNodeType(inserterNodeType)],
edges: [defaultWeightedEdgeType(assemblesEdgeType)],
nodes: new Map().set(
inserterNodeType.prefix,
defaultWeightedNodeType(inserterNodeType)
),
edges: new Map().set(
assemblesEdgeType.prefix,
defaultWeightedEdgeType(assemblesEdgeType)
),
};
const weights2 = {
nodes: [defaultWeightedNodeType(machineNodeType)],
edges: [defaultWeightedEdgeType(transportsEdgeType)],
nodes: new Map().set(
machineNodeType.prefix,
defaultWeightedNodeType(machineNodeType)
),
edges: new Map().set(
transportsEdgeType.prefix,
defaultWeightedEdgeType(transportsEdgeType)
),
};
expect(combineWeights([weights1, weights2])).toEqual(defaultWeights());
});
@ -62,20 +78,26 @@ describe("app/credExplorer/weights/weights", () => {
});
it("errors on duplicate edge prefix", () => {
const weights = {
nodes: [],
edges: [defaultWeightedEdgeType(assemblesEdgeType)],
nodes: new Map(),
edges: new Map().set(
assemblesEdgeType.prefix,
defaultWeightedEdgeType(assemblesEdgeType)
),
};
expect(() => combineWeights([weights, weights])).toThrowError(
"Duplicate prefix"
"duplicate key"
);
});
it("errors on duplicate node prefix", () => {
const weights = {
nodes: [defaultWeightedNodeType(inserterNodeType)],
edges: [],
nodes: new Map().set(
inserterNodeType.prefix,
defaultWeightedNodeType(inserterNodeType)
),
edges: new Map(),
};
expect(() => combineWeights([weights, weights])).toThrowError(
"Duplicate prefix"
"duplicate key"
);
});
});

View File

@ -7,11 +7,11 @@ import {NodeTrie, EdgeTrie} from "../../../core/trie";
export function weightsToEdgeEvaluator(weights: WeightedTypes): EdgeEvaluator {
const nodeTrie = new NodeTrie();
for (const {type, weight} of weights.nodes) {
for (const {type, weight} of weights.nodes.values()) {
nodeTrie.add(type.prefix, weight);
}
const edgeTrie = new EdgeTrie();
for (const {type, forwardWeight, backwardWeight} of weights.edges) {
for (const {type, forwardWeight, backwardWeight} of weights.edges.values()) {
edgeTrie.add(type.prefix, {forwardWeight, backwardWeight});
}

View File

@ -38,6 +38,7 @@ describe("app/credExplorer/weights/weightsToEdgeEvaluator", () => {
{weight: NullUtil.orElse(machine, 1), type: machineNodeType},
{weight: NullUtil.orElse(baseNode, 1), type: fallbackNodeType},
];
const nodesMap = new Map(nodes.map((x) => [x.type.prefix, x]));
const edges = [
{
forwardWeight: NullUtil.orElse(assemblesForward, 1),
@ -50,7 +51,8 @@ describe("app/credExplorer/weights/weightsToEdgeEvaluator", () => {
type: fallbackEdgeType,
},
];
return {nodes, edges};
const edgesMap = new Map(edges.map((x) => [x.type.prefix, x]));
return {nodes: nodesMap, edges: edgesMap};
}
function exampleEdgeWeights(weightArgs: WeightArgs) {
const ws = weights(weightArgs);