Skip to content

Commit

Permalink
Convert to Typescript ES2020 and fix exports
Browse files Browse the repository at this point in the history
  • Loading branch information
seriouscoderone committed Jul 11, 2023
1 parent 0fbc0ae commit a6d97ed
Show file tree
Hide file tree
Showing 19 changed files with 768 additions and 482 deletions.
Empty file added .npmignore
Empty file.
15 changes: 7 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,32 +1,31 @@
{
"name": "@qiwi/toposort",
"version": "1.0.1",
"version": "1.0.3",
"description": "Topological sort of directed ascyclic graphs (like dependecy lists)",
"main": "src/main/js/index.js",
"main": "dist/main/index.js",
"publishConfig": {
"access": "public"
},
"scripts": {
"prepublish": "tsc",
"style": "eslint src",
"style:fix": "yarn style --fix",
"verify": "yarn style && yarn test",
"test": "yarn test:unit",
"test:unit": "c8 -r html -r text -r lcov --exclude src/test uvu src/test/js"
"test:unit": "c8 -r html -r text -r lcov --exclude src/test uvu -r tsm src/test"
},
"files": [
"src/main",
"README.md",
"package.json"
],
"type": "module",
"types": "dist/main/index.d.ts",
"repository": {
"type": "git",
"url": "https://github.com/qiwi-forks/toposort.git"
},
"devDependencies": {
"@types/node": "^20.4.1",
"c8": "^7.12.0",
"eslint": "^8.32.0",
"eslint-config-qiwi": "^2.0.8",
"tsm": "^2.3.0",
"typescript": "^4.9.4",
"uvu": "^0.5.6"
},
Expand Down
3 changes: 1 addition & 2 deletions src/main/js/extra.js → src/main/extra.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
} from './helpers.js'
import { validateEdges, validateArgs, validateDag } from './validators.js'

export function toposortExtra (opts) {
export function toposortExtra (opts: { edges: unknown[][]; throwOnCycle?: boolean; nodes?: unknown[] }) {
validateArgs(opts)

const nodes = opts.nodes || uniqueNodes(opts.edges)
Expand Down Expand Up @@ -36,4 +36,3 @@ export function toposortExtra (opts) {
})
}
}

94 changes: 94 additions & 0 deletions src/main/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
export function makeOutgoingEdges(arr: unknown[][]) {
return arr.reduce((edges, [from, to]) => {
edges.has(from) || edges.set(from, new Set());
edges.has(to) || edges.set(to, new Set());
edges.get(from).add(to);
return edges;
}, new Map());
}

export function makeIncomingEdges(arr: unknown[][]) {
return arr.reduce((edges, [from, to]) => {
edges.has(from) || edges.set(from, new Set());
edges.has(to) || edges.set(to, new Set());
edges.get(to).add(from);
return edges;
}, new Map());
}

export function getStartNodes(edges: unknown[][]) {
const incomingEdges = makeIncomingEdges(edges);
const startNodes: unknown[] = [];
incomingEdges.forEach(
(value, key) => value.size === 0 && startNodes.push(key)
);

return startNodes;
}

export function makeNodesHash(arr: unknown[]) {
return new Map(arr.map((item, i) => [item, i]));
}

export function uniqueNodes(arr: unknown[][]) {
const res = new Set();
for (let i = 0, len = arr.length; i < len; i++) {
const edge = arr[i];
res.add(edge[0]);
res.add(edge[1]);
}
return [...res];
}

export function visitDepthFirst({
node,
adjacencyMap,
}: {
node: unknown;
adjacencyMap: Map<any, any>;
}) {
const visited = new Set();
const stack = [node];
let cur = node;
while (cur) {
visited.add(cur);
const neighbors = [...adjacencyMap.get(cur)];
stack.push(...neighbors.filter((item) => !visited.has(item)).reverse());
cur = stack.pop();
}
return visited;
}

export function getAdjacencyMapOfIndirectedGraph(edges: unknown[][]) {
return edges.reduce((acc, [from, to]) => {
[
[from, to],
[to, from],
].forEach(([node, neighbor]) => {
const neighbors = acc.get(node);
if (neighbors) {
neighbors.add(neighbor);
} else {
acc.set(node, new Set([neighbor]));
}
});
return acc;
}, new Map());
}

export function groupByComponents({ edges }: { edges: unknown[][] }) {
const adjacencyMap: Map<any, any> = getAdjacencyMapOfIndirectedGraph(edges);
const nodes = uniqueNodes(edges);
const components = [];
const visitedNodes = new Set();
let currentNode = nodes[0];

while (visitedNodes.size < nodes.length) {
const visited = visitDepthFirst({ adjacencyMap, node: currentNode });
components.push(visited);
visited.forEach((node) => visitedNodes.add(node));
currentNode = nodes.find((node) => !visitedNodes.has(node));
}

return components;
}
4 changes: 4 additions & 0 deletions src/main/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from "./extra.js";
export * from "./toposort.js";

export {toposortDefault as default} from "./toposort.js";
86 changes: 0 additions & 86 deletions src/main/js/helpers.js

This file was deleted.

2 changes: 0 additions & 2 deletions src/main/js/index.js

This file was deleted.

43 changes: 0 additions & 43 deletions src/main/js/validators.js

This file was deleted.

24 changes: 13 additions & 11 deletions src/main/js/toposort.js → src/main/toposort.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { makeNodesHash, makeOutgoingEdges, uniqueNodes } from './helpers.js'
import { validateEdges } from './validators.js'

function visitFactory(visited, outgoingEdges, nodesHash, sorted, cursor) {
function visit(node, i, predecessors) {
function visitFactory(visited: Record<string, boolean>, outgoingEdges: Map<unknown, Set<unknown>>, nodesHash: Map<unknown, number>, sorted: any[], cursor: number) {
function visit(node: unknown, i: number, predecessors: Set<unknown>) {
if (predecessors.has(node)) {
let nodeRep
try {
Expand All @@ -20,15 +20,15 @@ function visitFactory(visited, outgoingEdges, nodesHash, sorted, cursor) {
if (visited[i]) return
visited[i] = true

let outgoing = outgoingEdges.get(node) || new Set()
outgoing = [...outgoing]
const outgoingSet: (Set<unknown> | unknown[]) = outgoingEdges.get(node) || new Set()
const outgoing = [...outgoingSet]
i = outgoing.length

if (i) {
predecessors.add(node)
do {
const child = outgoing[--i]
visit(child, nodesHash.get(child), predecessors)
const child: any = outgoing[--i]
visit(child, nodesHash.get(child) as number, predecessors)
} while (i)
predecessors.delete(node)
}
Expand All @@ -39,11 +39,11 @@ function visitFactory(visited, outgoingEdges, nodesHash, sorted, cursor) {
return visit
}

export function toposortCore(nodes, edges) {
let cursor = nodes.length
function toposortCore(nodes: unknown[], edges: unknown[][]) {
const cursor = nodes.length
let i = cursor
const sorted = [cursor]
const visited = {}
const visited: Record<string, boolean> = {}
const nodesHash = makeNodesHash(nodes)
const outgoingEdges = makeOutgoingEdges(edges)
const visit = visitFactory(visited, outgoingEdges, nodesHash, sorted, cursor)
Expand All @@ -55,15 +55,17 @@ export function toposortCore(nodes, edges) {
return sorted
}

export function toposort(nodes, edges) {
export function toposort(nodes: unknown[], edges: unknown[][]) {
validateEdges(nodes, edges)

return toposortCore(nodes, edges)
}

export function toposortDefault(edges) {
export function toposortDefault(edges: unknown[][]) {
return toposort(uniqueNodes(edges), edges)
}

toposortDefault.array = toposort

export default toposortDefault

Loading

0 comments on commit a6d97ed

Please sign in to comment.