Replace `Graph.{getAdjacentEdges,getNeighborhood}` (#162)
`Graph.getAdjacentEdges` had a serious defect: for the adjacent edges, it's hard to tell which of the {src,dst} is the neighboring node address and which is the node we called `getAdjacentEdges` on. This commit fixes that limitation by replacing `getAdjacentEdges` with `getNeighborhood`, with a return signature of `{edge: Edge<EP>, neighborAddress: Address}[]` Some yak shaving was required: we needed a version of `expectSameSorted` and, by extension, `sortedByAddress` that takes an accessor to an Addressable, so that we could test that the neighborhoods returned were correct. To satisfy flow, I created `expectSameSortedAccessorized` and `sortedByAddressAccessorized`. Cumbersome, but it worked. ¯\_(ツ)_/¯
This commit is contained in:
parent
28e686c369
commit
678924087a
|
@ -220,16 +220,27 @@ export class Graph<NP, EP> {
|
|||
return result;
|
||||
}
|
||||
|
||||
getAdjacentEdges(
|
||||
/**
|
||||
* Find the neighborhood of a given nodeAddress
|
||||
*
|
||||
* By neighborhood, we mean every `{edge, neighborAddress}` such that edge
|
||||
* has `nodeAddress` as either `src` or `dst`, and `neighborAddress` is the
|
||||
* address at the other end of the edge.
|
||||
*/
|
||||
getNeighborhood(
|
||||
nodeAddress: Address,
|
||||
typeOptions?: {+nodeType?: string, +edgeType?: string}
|
||||
): Edge<EP>[] {
|
||||
const inEdges = this.getInEdges(nodeAddress, typeOptions);
|
||||
): {+edge: Edge<EP>, +neighborAddress: Address}[] {
|
||||
const inNeighbors = this.getInEdges(nodeAddress, typeOptions).map((e) => {
|
||||
return {edge: e, neighborAddress: e.src};
|
||||
});
|
||||
const outNeighbors = this.getOutEdges(nodeAddress, typeOptions)
|
||||
// If there are self-reference edges, avoid double counting them.
|
||||
const outEdges = this.getOutEdges(nodeAddress, typeOptions).filter(
|
||||
(e) => !deepEqual(e.src, e.dst)
|
||||
);
|
||||
return [].concat(inEdges, outEdges);
|
||||
.filter((e) => !deepEqual(e.src, e.dst))
|
||||
.map((e) => {
|
||||
return {edge: e, neighborAddress: e.dst};
|
||||
});
|
||||
return [].concat(inNeighbors, outNeighbors);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -251,9 +251,9 @@ describe("graph", () => {
|
|||
expect(g.getInEdges(demoData.crabNode().address)).toContainEqual(
|
||||
demoData.crabLoopEdge()
|
||||
);
|
||||
const crabAdjacencies = g.getAdjacentEdges(demoData.crabNode().address);
|
||||
const crabLoops = crabAdjacencies.filter((e) =>
|
||||
deepEqual(e, demoData.crabLoopEdge())
|
||||
const crabNeighbors = g.getNeighborhood(demoData.crabNode().address);
|
||||
const crabLoops = crabNeighbors.filter(({edge, neighborAddress}) =>
|
||||
deepEqual(edge, demoData.crabLoopEdge())
|
||||
);
|
||||
expect(crabLoops).toHaveLength(1);
|
||||
});
|
||||
|
@ -414,9 +414,12 @@ describe("graph", () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
describe("adjacentEdges", () => {
|
||||
describe("getNeighborhood", () => {
|
||||
const eg = new ExampleGraph();
|
||||
const getEdges = (opts) => eg.graph.getAdjacentEdges(eg.root, opts);
|
||||
const getEdges = (opts) =>
|
||||
eg.graph
|
||||
.getNeighborhood(eg.root, opts)
|
||||
.map(({edge, neighborAddress}) => edge);
|
||||
const allEdges = [
|
||||
eg.inEdges.a1,
|
||||
eg.inEdges.a2,
|
||||
|
@ -455,46 +458,96 @@ describe("graph", () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
it("gets adjacent-edges", () => {
|
||||
const nodeAndExpectedEdgePairs = [
|
||||
[
|
||||
demoData.heroNode(),
|
||||
[
|
||||
demoData.eatEdge(),
|
||||
demoData.pickEdge(),
|
||||
demoData.grabEdge(),
|
||||
demoData.cookEdge(),
|
||||
demoData.duplicateCookEdge(),
|
||||
it("gets neighborhood", () => {
|
||||
const nodeAndNeighborhood = [
|
||||
{
|
||||
node: demoData.heroNode(),
|
||||
neighborhood: [
|
||||
{
|
||||
edge: demoData.eatEdge(),
|
||||
neighborAddress: demoData.mealNode().address,
|
||||
},
|
||||
{
|
||||
edge: demoData.pickEdge(),
|
||||
neighborAddress: demoData.bananasNode().address,
|
||||
},
|
||||
{
|
||||
edge: demoData.grabEdge(),
|
||||
neighborAddress: demoData.crabNode().address,
|
||||
},
|
||||
{
|
||||
edge: demoData.cookEdge(),
|
||||
neighborAddress: demoData.mealNode().address,
|
||||
},
|
||||
{
|
||||
edge: demoData.duplicateCookEdge(),
|
||||
neighborAddress: demoData.mealNode().address,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
node: demoData.bananasNode(),
|
||||
neighborhood: [
|
||||
{
|
||||
edge: demoData.pickEdge(),
|
||||
neighborAddress: demoData.heroNode().address,
|
||||
},
|
||||
{
|
||||
edge: demoData.bananasIngredientEdge(),
|
||||
neighborAddress: demoData.mealNode().address,
|
||||
},
|
||||
],
|
||||
[
|
||||
demoData.bananasNode(),
|
||||
[demoData.pickEdge(), demoData.bananasIngredientEdge()],
|
||||
],
|
||||
[
|
||||
demoData.crabNode(),
|
||||
[
|
||||
demoData.crabIngredientEdge(),
|
||||
demoData.grabEdge(),
|
||||
demoData.crabLoopEdge(),
|
||||
],
|
||||
],
|
||||
[
|
||||
demoData.mealNode(),
|
||||
[
|
||||
demoData.bananasIngredientEdge(),
|
||||
demoData.crabIngredientEdge(),
|
||||
demoData.cookEdge(),
|
||||
demoData.eatEdge(),
|
||||
demoData.duplicateCookEdge(),
|
||||
},
|
||||
{
|
||||
node: demoData.crabNode(),
|
||||
neighborhood: [
|
||||
{
|
||||
edge: demoData.crabIngredientEdge(),
|
||||
neighborAddress: demoData.mealNode().address,
|
||||
},
|
||||
{
|
||||
edge: demoData.grabEdge(),
|
||||
neighborAddress: demoData.heroNode().address,
|
||||
},
|
||||
{
|
||||
edge: demoData.crabLoopEdge(),
|
||||
neighborAddress: demoData.crabNode().address,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
node: demoData.mealNode(),
|
||||
neighborhood: [
|
||||
{
|
||||
edge: demoData.bananasIngredientEdge(),
|
||||
neighborAddress: demoData.bananasNode().address,
|
||||
},
|
||||
{
|
||||
edge: demoData.crabIngredientEdge(),
|
||||
neighborAddress: demoData.crabNode().address,
|
||||
},
|
||||
{
|
||||
edge: demoData.cookEdge(),
|
||||
neighborAddress: demoData.heroNode().address,
|
||||
},
|
||||
{
|
||||
edge: demoData.eatEdge(),
|
||||
neighborAddress: demoData.heroNode().address,
|
||||
},
|
||||
{
|
||||
edge: demoData.duplicateCookEdge(),
|
||||
neighborAddress: demoData.heroNode().address,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
nodeAndExpectedEdgePairs.forEach(([node, expectedEdges]) => {
|
||||
nodeAndNeighborhood.forEach(({node, neighborhood}) => {
|
||||
const actual = demoData
|
||||
.advancedMealGraph()
|
||||
.getAdjacentEdges(node.address);
|
||||
expectSameSorted(actual, expectedEdges);
|
||||
.getNeighborhood(node.address);
|
||||
const sort = (hood) =>
|
||||
sortBy(hood, (hoodlum) => stringify(hoodlum.edge.address));
|
||||
expect(sort(actual)).toEqual(sort(neighborhood));
|
||||
});
|
||||
});
|
||||
it("gets out-edges", () => {
|
||||
|
|
Loading…
Reference in New Issue