diff --git a/lib/comparisonView.js b/lib/comparisonView.js
index f3c4259..968a096 100644
--- a/lib/comparisonView.js
+++ b/lib/comparisonView.js
@@ -1,7 +1,7 @@
/* Imports */
import Head from 'next/head'
import React, { useState } from "react";
-import { DrawGraph, removeOldSvg } from './labeledgraph';
+import { DrawGraph, removeOldSvg } from './labeledGraph';
import { SubmitSliderButton } from "./slider";
import { DisplayElement } from './displayElement'
import { DisplayAsMarkdown } from './displayAsMarkdown'
diff --git a/lib/findPaths.js b/lib/findPaths.js
index f5878ee..77b348e 100644
--- a/lib/findPaths.js
+++ b/lib/findPaths.js
@@ -1,49 +1,55 @@
/* Imports*/
-import React, { useState, useEffect } from 'react';
-import { toLocale, truncateValueForDisplay, numToAlphabeticalString, formatLargeOrSmall } from "../lib/utils.js"
+import React from 'react';
+import { numToAlphabeticalString, formatLargeOrSmall, avg } from "../lib/utils.js"
-/* Utilities */
-let avg = arr => arr.reduce((a, b) => (a + b), 0) / arr.length
+/* Functions */
-/* Main function */
+const pathPlusLink = (pathSoFar, link) => {
+ return [...pathSoFar, link]
+ // previously: pathSoFar.concat(link).flat()
+ // Note that concat is not destructive
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat
+}
-async function findPathsInner({
- sourceElementId, sourceElementPosition,
- targetElementId, targetElementPosition,
- links, nodes,
- maxLengthOfPath, pathSoFar
+async function findPathsWithoutPrunning({
+ sourceElementId, targetElementId,
+ maxLengthOfPath, pathSoFar,
+ links, nodes
}) {
+ // This is an un-used function which might make findPaths more understandable
+ // It uses the same recursive functionality
+ // but has no path prunning
let paths = []
- let minPos = Math.min(sourceElementPosition, targetElementPosition)
- let maxPos = Math.max(sourceElementPosition, targetElementPosition)
- let linksInner = links.filter(link =>
- (minPos <= link.sourceElementPosition && link.sourceElementPosition <= maxPos) &&
- (minPos <= link.targetElementPosition && link.targetElementPosition <= maxPos)
- )
- let linksNow = linksInner.filter(link => (link.source == sourceElementId || link.target == sourceElementId))
+
+ /* Path traversing */
if (maxLengthOfPath > 0) {
- for (let link of linksNow) {
+ for (let link of links) { // vs let link of linksNow in findPaths
if (
((link.source == sourceElementId) && (link.target == targetElementId)) ||
((link.source == targetElementId) && (link.target == sourceElementId))
- ) {
- paths.push(pathSoFar.concat(link).flat())
+ ) { // direct Path
+ let newPath = pathPlusLink(pathSoFar, link)
+ paths.push(newPath)
} else if ((link.source == sourceElementId)) {
- let newPaths = await findPathsInner({
- sourceElementId: link.target, sourceElementPosition: link.sourceElementPosition,
- targetElementId, targetElementPosition,
- pathSoFar: pathSoFar.concat(link).flat(),
- links: linksInner, nodes, maxLengthOfPath: (maxLengthOfPath - 1)
+ let newPaths = await findPaths({
+ pathSoFar: pathPlusLink(pathSoFar, link),
+ maxLengthOfPath: (maxLengthOfPath - 1),
+ sourceElementId: link.target,
+ targetElementId,
+ links: links, // vs let link of linksInner in findPaths
+ nodes
})
- if (newPaths.length != 0) {
+ if (newPaths.length != 0) {
paths.push(...newPaths)
}
} else if ((link.target == sourceElementId)) {
- let newPaths = await findPathsInner({
- sourceElementId: link.source, sourceElementPosition: link.sourceElementPosition,
- targetElementId, targetElementPosition,
- pathSoFar: pathSoFar.concat(link).flat(),
- links: linksInner, nodes, maxLengthOfPath: (maxLengthOfPath - 1)
+ let newPaths = await findPaths({
+ pathSoFar: pathPlusLink(pathSoFar, link),
+ maxLengthOfPath: (maxLengthOfPath - 1),
+ sourceElementId: link.source,
+ targetElementId,
+ links: links, // vs let link of linksInner in findPaths
+ nodes
})
if (newPaths.length != 0) {
paths.push(...newPaths)
@@ -54,40 +60,93 @@ async function findPathsInner({
return paths
}
-/*
-function findPaths({
+
+async function findPaths({
sourceElementId, sourceElementPosition,
targetElementId, targetElementPosition,
- nodes, links, direction
+ maxLengthOfPath, pathSoFar,
+ links, nodes
}) {
- let positionSourceElement = nodes.map((element, i) => (element.id)).indexOf(sourceElementId)
- let positionTargetElement = nodes.map((element, i) => (element.id)).indexOf(targetElementId)
- let maxLengthOfPath = Math.abs(positionSourceElement - positionTargetElement)
- return findPathsInner({
- sourceElementId, sourceElementPosition,
- targetElementId, targetElementPosition,
- links, nodes, direction,
- maxLengthOfPath, pathSoFar: []
- })
+ // This is the key path finding function
+ // It finds the path from one element to another, recursively
+ // It used to be very computationally expensive until I added
+ // the path prunning step: Instead of traversing all links,
+ // traverse only those which are between the origin and target links
+ // this requires us to have a notion of "between"
+
+ let paths = []
+
+ /* Path prunning*/
+ let minPos = Math.min(sourceElementPosition, targetElementPosition)
+ let maxPos = Math.max(sourceElementPosition, targetElementPosition)
+ let linksInner = links.filter(link =>
+ (minPos <= link.sourceElementPosition && link.sourceElementPosition <= maxPos) &&
+ (minPos <= link.targetElementPosition && link.targetElementPosition <= maxPos)
+ )
+ let linksNow = linksInner.filter(link => (link.source == sourceElementId || link.target == sourceElementId))
+
+ /* Path traversing */
+ if (maxLengthOfPath > 0) {
+ for (let link of linksNow) {
+ if (
+ ((link.source == sourceElementId) && (link.target == targetElementId)) ||
+ ((link.source == targetElementId) && (link.target == sourceElementId))
+ ) { // direct Path
+ let newPath = pathPlusLink(pathSoFar, link)
+ paths.push(newPath)
+ } else if ((link.source == sourceElementId)) {
+ let newPaths = await findPaths({
+ pathSoFar: pathPlusLink(pathSoFar, link),
+ maxLengthOfPath: (maxLengthOfPath - 1),
+ sourceElementPosition: link.sourceElementPosition,
+ sourceElementId: link.target,
+ targetElementId, targetElementPosition,
+ links: linksInner,
+ nodes
+ })
+ if (newPaths.length != 0) {
+ paths.push(...newPaths)
+ }
+ } else if ((link.target == sourceElementId)) {
+ let newPaths = await findPaths({
+ pathSoFar: pathPlusLink(pathSoFar, link),
+ maxLengthOfPath: (maxLengthOfPath - 1),
+ sourceElementPosition: link.sourceElementPosition,
+ sourceElementId: link.source,
+ targetElementPosition,
+ targetElementId,
+ links: linksInner,
+ nodes
+ })
+ if (newPaths.length != 0) {
+ paths.push(...newPaths)
+ }
+ }
+ }
+ }
+
+ return paths
}
-*/
async function findDistance({
sourceElementId, sourceElementPosition,
targetElementId, targetElementPosition,
- nodes, links, direction
+ nodes, links
}) {
+ // This function gets all possible paths using findPaths
+ // then orders them correctly in the for loop
+ // (by flipping the distance to 1/distance when necessary)
+ // and then gets the array of weights for the different paths.
+ console.log(`findDistance@findPaths.js from ${sourceElementPosition} to ${targetElementPosition}`)
+
let maxLengthOfPath = Math.abs(sourceElementPosition - targetElementPosition)
- let paths = await findPathsInner({
+ let paths = await findPaths({
sourceElementId, sourceElementPosition,
targetElementId, targetElementPosition,
- links, nodes, direction,
+ links, nodes,
maxLengthOfPath, pathSoFar: []
});
- console.log(`findDistance from ${sourceElementPosition} to ${targetElementPosition}`)
- console.log(targetElementId)
- console.log(direction)
- console.log(paths)
+
let weights = []
for (let path of paths) {
let currentSource = sourceElementId
@@ -106,25 +165,26 @@ async function findDistance({
}
weights.push(weight)
}
- //let sum = JSON.stringify(weights)//weights.length > 0 ? weights.reduce((a,b) => (a+b)) / weights.length : "Not found"
- //return sum
return weights
- //return weights.map(weight => Math.round(weight*100)/100)
}
async function findDistancesForAllElements({ nodes, links }) {
+ // Simple wrapper function around findDistance
+ // Needs to find the reference point first
+ console.log("findDistancesForAllElements@findPaths.js")
+ /* Get or build reference element */
let referenceElements = nodes.filter(x => x.isReferenceValue)
let midpoint = Math.round(nodes.length / 2)
let referenceElement = referenceElements.length > 0 ? referenceElements[0] : nodes[midpoint]
- // console.log(nodes)
console.log(`referenceElement.position: ${referenceElement.position}`)
+
+ /* Get distances. */
let distances = nodes.map(node => {
if (node.isReferenceValue || (node.id == referenceElement.id)) {
return [1]
} else {
console.log("node")
console.log(node)
- let isUpwardsDirection = referenceElement.position < node.position
let distance = findDistance({
sourceElementId: referenceElement.id,
sourceElementPosition: referenceElement.position,
@@ -132,7 +192,6 @@ async function findDistancesForAllElements({ nodes, links }) {
targetElementPosition: node.position,
nodes: nodes,
links: links,
- direction: isUpwardsDirection ? "upwards" : "downwards"
})
return distance
}
@@ -141,23 +200,11 @@ async function findDistancesForAllElements({ nodes, links }) {
return distances
}
-function abridgeArrayAndDisplay(array) {
- let newArray
- let formatForDisplay
- if (array.length > 10) {
- newArray = array.slice(0, 9)
- formatForDisplay = newArray.map(d => formatLargeOrSmall(d))
- formatForDisplay[9] = "..."
- } else {
- newArray = array
- formatForDisplay = newArray.map(d => formatLargeOrSmall(d))
- }
- let result = JSON.stringify(formatForDisplay, null, 2).replaceAll(`"`, "")
- return result
-}
-
export async function buildRows({ isListOrdered, orderedList, listOfElements, links, rows, setTableRows }) {
- // Not used yet.
+ console.log("buildRows@findPaths.js")
+ // This function is used in pages/comparisonView.js to create the rows that will be displayed.
+ // it is in there because it needs to be deployed after isListOrdered becomes true,
+ // and using an useEffect inside CreateTable was too messy.
if (isListOrdered && !(orderedList.length < listOfElements.length) && rows.length == 0) {
let nodes = []
let positionDictionary = ({})
@@ -165,8 +212,6 @@ export async function buildRows({ isListOrdered, orderedList, listOfElements, li
nodes.push({ ...listOfElements[id], position: pos })
positionDictionary[id] = pos
})
- // let nodes = orderedList.map((id, pos) => ({ ...listOfElements[id], position: pos }))
- /* Pre-process links to talk in terms of distances */
links = links.map(link => ({
...link,
sourceElementPosition: positionDictionary[link.source],
@@ -180,161 +225,63 @@ export async function buildRows({ isListOrdered, orderedList, listOfElements, li
name: element.name,
distances: distances[i]
}))
- console.log("rows@CreateTableWithDistances")
console.log(rows)
setTableRows(rows)
- } else {
- // rows = []
- // Do nothing
}
- // return rows
}
-export function CreateTableWithDistancesWithUseEffect({ isListOrdered, orderedList, listOfElements, links, tableRows, setTableRows }) {
-
- useEffect(async () => {
- await buildRows({ isListOrdered, orderedList, listOfElements, links, rows: tableRows, setTableRows })
- /*
- // https://stackoverflow.com/questions/57847626/using-async-await-inside-a-react-functional-component
- // https://stackoverflow.com/questions/54936559/using-async-await-in-react-component
- // https://reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects
- if (isListOrdered && ! (orderedList.length < listOfElements.length) && rows.length == 0) {
- let nodes = []
- let positionDictionary=({})
- orderedList.forEach((id, pos) => {
- nodes.push({ ...listOfElements[id], position: pos })
- positionDictionary[id] = pos
- })
- // let nodes = orderedList.map((id, pos) => ({ ...listOfElements[id], position: pos }))
- // Pre-process links to talk in terms of distances
- links = links.map(link => ({...link,
- sourceElementPosition: positionDictionary[link.source],
- targetElementPosition: positionDictionary[link.target]
- }))
-
- let distances = await findDistancesForAllElements({ nodes, links })
- setRows(nodes.map((element, i) => ({
- id: numToAlphabeticalString(element.position),
- position: element.position,
- name: element.name,
- distances: distances[i]
- })))
- console.log("rows@CreateTableWithDistances")
- console.log(rows)
+
+export function CreateTable({ tableRows }) {
+ /* This function receives a list of rows, and displays them nicely. */
+ function abridgeArrayAndDisplay(array) {
+ let newArray
+ let formatForDisplay
+ if (array.length > 10) {
+ newArray = array.slice(0, 9)
+ formatForDisplay = newArray.map(d => formatLargeOrSmall(d))
+ formatForDisplay[9] = "..."
+ } else {
+ newArray = array
+ formatForDisplay = newArray.map(d => formatLargeOrSmall(d))
}
- */
- }); // this useEffect should ideally only work when isListOrdered changes, but I haven't bothered to program that.
-
-
- return (
-
-
-
-
- Id |
- |
- Position |
- |
- Element |
- |
- Possible relative values |
- |
- Average relative value |
-
-
-
- {tableRows.map(row =>
- {row.id} |
- |
- {row.position} |
- |
- {row.name} |
- |
- {abridgeArrayAndDisplay(row.distances)} |
- |
- {formatLargeOrSmall(avg(row.distances))} |
-
- )}
-
-
-
- )
-
-}
-
-export function CreateTableWithDistances({ tableRows }) {
- return (
-
-
-
-
- Id |
- |
- Position |
- |
- Element |
- |
- Possible relative values |
- |
- Average relative value |
-
-
-
- {tableRows.map(row =>
- {row.id} |
- |
- {row.position} |
- |
- {row.name} |
- |
- {abridgeArrayAndDisplay(row.distances)} |
- |
- {formatLargeOrSmall(avg(row.distances))} |
-
- )}
-
-
-
- )
-
-}
-
-function CreateTableWithoutDistances({ isListOrdered, orderedList, listOfElements, links }) {
- if (!isListOrdered || orderedList.length < listOfElements.length) {
- return ({""}
)
- } else {
- let nodes = orderedList.map(i => listOfElements[i])
- let rows = nodes.map((element, i) => ({ id: numToAlphabeticalString(element.id), name: element.name }))
- console.log("rows@CreateTableWithoutDistances")
- console.log(rows)
- return (
-
-
-
-
- Id |
- |
- Element |
- |
-
-
-
- {rows.map(row =>
- {row.id} |
- |
- {row.name} |
-
- )}
- { }
-
-
-
- )
+ let result = JSON.stringify(formatForDisplay, null, 2).replaceAll(`"`, "")
+ return result
}
+ return (
+
+
+
+
+ Id |
+ |
+ Position |
+ |
+ Element |
+ |
+ Possible relative values |
+ |
+ Average relative value |
+
+
+
+ {tableRows.map(row =>
+ {row.id} |
+ |
+ {row.position} |
+ |
+ {row.name} |
+ |
+ {abridgeArrayAndDisplay(row.distances)} |
+ |
+ {formatLargeOrSmall(avg(row.distances))} |
+
+ )}
+
+
+
+ )
}
-export const CreateTable = CreateTableWithDistances // CreateTableWithoutDistances;
-
/* Testing */
//import fs from 'fs';
//import path from 'path';
@@ -343,7 +290,7 @@ const directory = path.join(process.cwd(),"pages")
let links = JSON.parse(fs.readFileSync(path.join(directory, 'distancesExample.json'), 'utf8'));
let nodes = JSON.parse(fs.readFileSync(path.join(directory, 'listOfPosts.json'), 'utf8'));
-let paths = findPathsInner({sourceElementId:2, targetElementId:0, pathSoFar: [], links, nodes, maxLengthOfPath: 2})
+let paths = findPaths({sourceElementId:2, targetElementId:0, pathSoFar: [], links, nodes, maxLengthOfPath: 2})
console.log(JSON.stringify(paths, null, 2))
*/
/*
diff --git a/lib/labeledgraph.js b/lib/labeledGraph.js
similarity index 98%
rename from lib/labeledgraph.js
rename to lib/labeledGraph.js
index 3259dd3..011adc5 100644
--- a/lib/labeledgraph.js
+++ b/lib/labeledGraph.js
@@ -1,6 +1,6 @@
import React, { useState, useEffect } from "react";
import * as d3 from 'd3';
-import { toLocale, truncateValueForDisplay, numToAlphabeticalString, formatLargeOrSmall } from "../lib/utils.js"
+import { toLocale, truncateValueForDisplay, numToAlphabeticalString, formatLargeOrSmall } from "./utils.js"
let getlength = (number) => number.toString().length;
diff --git a/lib/utils.js b/lib/utils.js
index 2e4f9a0..f186e57 100644
--- a/lib/utils.js
+++ b/lib/utils.js
@@ -67,4 +67,6 @@ export function expectedNumMergeSortSteps(n) {
} else {
return Math.ceil((n ** 2) / (n + 2)) + expectedNumMergeSortSteps(Math.ceil(n / 2))
}
-}
\ No newline at end of file
+}
+
+export const avg = arr => arr.reduce((a, b) => (a + b), 0) / arr.length