@pipepack/graph
Version:
A graph data structure with pipepack related algorithm.
163 lines (127 loc) • 4.08 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
// interface
class Graph {
// nodes mean key, edges mean value
// Computes a string encoding of an edge,
// for use as a key in an object.
static encodeEdge(u, v) {
return `${u}|${v}`;
}
constructor(serialized) {
this.edges = new Map();
this.edgeWeights = new Map(); // If a serialized graph was passed into the constructor, deserialize it.
if (serialized) {
this.deserialize(serialized);
}
} // gets the adjacent node list for the given node as access layer
adjacent(nid) {
return this.edges.get(nid) || new Set();
} // maybe better when implment standard iterator
nodes() {
const deduplication = new Set();
this.edges.forEach((adjacents, nid) => {
// node self
deduplication.add(nid); // node adjacents
adjacents.forEach(aid => {
deduplication.add(aid);
});
});
return Array.from(deduplication);
} // adds a node to the graph.
// If node was already added, this function does nothing.
// If node was not already added, this function sets up an empty adjacency list.
addNode(nid) {
this.edges.set(nid, this.adjacent(nid));
return this;
} // removes a node from the graph, also removes incoming and outgoing edges.
removeNode(nid) {
// delete node self
this.edges.delete(nid); // delete adjacent link
this.edges.forEach(adjacents => {
adjacents.delete(nid);
});
return this;
} // directional edige
addEdge(u, v, weight) {
this.addNode(u);
this.addNode(v); // node 'u' already within internal storage, without worrying about lost state
this.adjacent(u).add(v);
this.setEdgeWeight(u, v, weight);
return this;
} // Removes the edge from node u to node v.
// Does not remove the nodes.
// Does nothing if the edge does not exist.
removeEdge(u, v) {
// remove adjacent node when source node exist, nothing when not exist
this.adjacent(u).delete(u); // remove edge weight if any
this.edgeWeights.delete(Graph.encodeEdge(u, v));
return this;
} // gets the weight of the given edge, returns 1 if no weight was previously set.
// use number as weights just now
getEdgeWeight(u, v) {
return this.edgeWeights.get(Graph.encodeEdge(u, v));
} // sets the weight of the given edge.
setEdgeWeight(u, v, weight) {
if (weight) {
this.edgeWeights.set(Graph.encodeEdge(u, v), weight);
}
return this;
} // omit weight property when undefined, simplify serialize
wrapEdgeWeight(u, v) {
const weight = this.getEdgeWeight(u, v);
const required = {
source: u,
target: v
};
const optional = weight ? {} : {
weight
};
return { ...required,
...optional
};
}
serialize() {
const ids = this.nodes();
const nodes = ids.map(id => ({
id
}));
const edges = ids.reduce((acc, id) => {
// use mangle prefix for better semantic link
// eslint-disable-next-line @typescript-eslint/naming-convention, no-underscore-dangle
const _edges = [];
this.adjacent(id).forEach(aid => {
_edges.push(this.wrapEdgeWeight(id, aid));
});
return [...acc, ..._edges];
}, []);
return {
nodes,
edges
};
} // Deserializes the given serialized graph
deserialize(serialized) {
serialized.nodes.forEach(node => {
this.addNode(node.id);
});
serialized.edges.forEach(edge => {
this.addEdge(edge.source, edge.target, edge.weight);
});
return this;
} // computes the indegree for the given node.
// not very efficient, costs O(E) where E = number of edges.
indegree(nid) {
return Array.from(this.edges.values()).reduce((acc, curr) => {
return curr.has(nid) ? acc + 1 : acc;
}, 0);
} // computes the outdegree for the given node.
outdegree(nid) {
return this.adjacent(nid).size;
}
}
var _default = Graph;
exports.default = _default;
//# sourceMappingURL=Graph.js.map