Add Graph.getAdjacentEdges for in and out edges (#157)

Some consumers of the graph may prefer to treat it as an undirected
graph. For example, when finding the author of an issue, it is wholly
sufficient to find an edge with the `AUTHORS` type; the caller may
prefer not to be bothered with remembering which end of the `AUTHORS`
end is considered the `src` and which is the `dst`.

The `getAdjacentEdges` call enables that, by combining the output of
`getInEdges` and `getOutEdges`.

Test plan:
The new tests are pretty comprehensive.
This commit is contained in:
Dandelion Mané 2018-04-26 20:27:46 -07:00 committed by GitHub
parent aa071ceab3
commit 8e9ddbf9fc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 101 additions and 0 deletions

View File

@ -220,6 +220,18 @@ export class Graph<NP, EP> {
return result;
}
getAdjacentEdges(
nodeAddress: Address,
typeOptions?: {+nodeType?: string, +edgeType?: string}
): Edge<EP>[] {
const inEdges = this.getInEdges(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);
}
/**
* Gets all nodes in the graph, in unspecified order.
*/

View File

@ -1,5 +1,7 @@
// @flow
import deepEqual from "lodash.isequal";
import type {Address, Addressable} from "./address";
import {sortedByAddress} from "./address";
import type {Node, Edge} from "./graph";
@ -247,6 +249,11 @@ 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())
);
expect(crabLoops).toHaveLength(1);
});
it("allows creating multiple edges between the same nodes", () => {
@ -405,6 +412,88 @@ describe("graph", () => {
});
});
});
describe("adjacentEdges", () => {
const eg = new ExampleGraph();
const getEdges = (opts) => eg.graph.getAdjacentEdges(eg.root, opts);
const allEdges = [
eg.inEdges.a1,
eg.inEdges.a2,
eg.inEdges.b1,
eg.inEdges.b2,
eg.outEdges.a1,
eg.outEdges.a2,
eg.outEdges.b1,
eg.outEdges.b2,
];
it("typefiltering is optional", () => {
expectSameSorted(getEdges(), allEdges);
expectSameSorted(getEdges({}), allEdges);
});
it("filters on node types", () => {
expectSameSorted(getEdges({nodeType: "A"}), [
eg.outEdges.a1,
eg.outEdges.a2,
eg.inEdges.a1,
eg.inEdges.a2,
]);
});
it("filters on edge types", () => {
expectSameSorted(getEdges({edgeType: "1"}), [
eg.outEdges.a1,
eg.outEdges.b1,
eg.inEdges.a1,
eg.inEdges.b1,
]);
});
it("filters on node and edge types", () => {
expectSameSorted(getEdges({nodeType: "A", edgeType: "1"}), [
eg.outEdges.a1,
eg.inEdges.a1,
]);
});
});
});
it("gets adjacent-edges", () => {
const nodeAndExpectedEdgePairs = [
[
demoData.heroNode(),
[
demoData.eatEdge(),
demoData.pickEdge(),
demoData.grabEdge(),
demoData.cookEdge(),
demoData.duplicateCookEdge(),
],
],
[
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(),
],
],
];
nodeAndExpectedEdgePairs.forEach(([node, expectedEdges]) => {
const actual = demoData
.advancedMealGraph()
.getAdjacentEdges(node.address);
expectSameSorted(actual, expectedEdges);
});
});
it("gets out-edges", () => {
const nodeAndExpectedEdgePairs = [