chore: Refactored and added comments

This commit is contained in:
NunoSempere 2021-12-08 12:35:18 +01:00
parent 3d6a544a72
commit 849a09207e
4 changed files with 174 additions and 225 deletions

View File

@ -1,7 +1,7 @@
/* Imports */ /* Imports */
import Head from 'next/head' import Head from 'next/head'
import React, { useState } from "react"; import React, { useState } from "react";
import { DrawGraph, removeOldSvg } from './labeledgraph'; import { DrawGraph, removeOldSvg } from './labeledGraph';
import { SubmitSliderButton } from "./slider"; import { SubmitSliderButton } from "./slider";
import { DisplayElement } from './displayElement' import { DisplayElement } from './displayElement'
import { DisplayAsMarkdown } from './displayAsMarkdown' import { DisplayAsMarkdown } from './displayAsMarkdown'

View File

@ -1,49 +1,55 @@
/* Imports*/ /* Imports*/
import React, { useState, useEffect } from 'react'; import React from 'react';
import { toLocale, truncateValueForDisplay, numToAlphabeticalString, formatLargeOrSmall } from "../lib/utils.js" import { numToAlphabeticalString, formatLargeOrSmall, avg } from "../lib/utils.js"
/* Utilities */ /* Functions */
let avg = arr => arr.reduce((a, b) => (a + b), 0) / arr.length
/* 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({ async function findPathsWithoutPrunning({
sourceElementId, sourceElementPosition, sourceElementId, targetElementId,
targetElementId, targetElementPosition, maxLengthOfPath, pathSoFar,
links, nodes, links, nodes
maxLengthOfPath, pathSoFar
}) { }) {
// 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 paths = []
let minPos = Math.min(sourceElementPosition, targetElementPosition)
let maxPos = Math.max(sourceElementPosition, targetElementPosition) /* Path traversing */
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))
if (maxLengthOfPath > 0) { if (maxLengthOfPath > 0) {
for (let link of linksNow) { for (let link of links) { // vs let link of linksNow in findPaths
if ( if (
((link.source == sourceElementId) && (link.target == targetElementId)) || ((link.source == sourceElementId) && (link.target == targetElementId)) ||
((link.source == targetElementId) && (link.target == sourceElementId)) ((link.source == targetElementId) && (link.target == sourceElementId))
) { ) { // direct Path
paths.push(pathSoFar.concat(link).flat()) let newPath = pathPlusLink(pathSoFar, link)
paths.push(newPath)
} else if ((link.source == sourceElementId)) { } else if ((link.source == sourceElementId)) {
let newPaths = await findPathsInner({ let newPaths = await findPaths({
sourceElementId: link.target, sourceElementPosition: link.sourceElementPosition, pathSoFar: pathPlusLink(pathSoFar, link),
targetElementId, targetElementPosition, maxLengthOfPath: (maxLengthOfPath - 1),
pathSoFar: pathSoFar.concat(link).flat(), sourceElementId: link.target,
links: linksInner, nodes, maxLengthOfPath: (maxLengthOfPath - 1) targetElementId,
links: links, // vs let link of linksInner in findPaths
nodes
}) })
if (newPaths.length != 0) { if (newPaths.length != 0) {
paths.push(...newPaths) paths.push(...newPaths)
} }
} else if ((link.target == sourceElementId)) { } else if ((link.target == sourceElementId)) {
let newPaths = await findPathsInner({ let newPaths = await findPaths({
sourceElementId: link.source, sourceElementPosition: link.sourceElementPosition, pathSoFar: pathPlusLink(pathSoFar, link),
targetElementId, targetElementPosition, maxLengthOfPath: (maxLengthOfPath - 1),
pathSoFar: pathSoFar.concat(link).flat(), sourceElementId: link.source,
links: linksInner, nodes, maxLengthOfPath: (maxLengthOfPath - 1) targetElementId,
links: links, // vs let link of linksInner in findPaths
nodes
}) })
if (newPaths.length != 0) { if (newPaths.length != 0) {
paths.push(...newPaths) paths.push(...newPaths)
@ -54,40 +60,93 @@ async function findPathsInner({
return paths return paths
} }
/*
function findPaths({ async function findPaths({
sourceElementId, sourceElementPosition, sourceElementId, sourceElementPosition,
targetElementId, targetElementPosition, targetElementId, targetElementPosition,
nodes, links, direction maxLengthOfPath, pathSoFar,
links, nodes
}) { }) {
let positionSourceElement = nodes.map((element, i) => (element.id)).indexOf(sourceElementId) // This is the key path finding function
let positionTargetElement = nodes.map((element, i) => (element.id)).indexOf(targetElementId) // It finds the path from one element to another, recursively
let maxLengthOfPath = Math.abs(positionSourceElement - positionTargetElement) // It used to be very computationally expensive until I added
return findPathsInner({ // the path prunning step: Instead of traversing all links,
sourceElementId, sourceElementPosition, // traverse only those which are between the origin and target links
targetElementId, targetElementPosition, // this requires us to have a notion of "between"
links, nodes, direction,
maxLengthOfPath, pathSoFar: [] 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({ async function findDistance({
sourceElementId, sourceElementPosition, sourceElementId, sourceElementPosition,
targetElementId, targetElementPosition, 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 maxLengthOfPath = Math.abs(sourceElementPosition - targetElementPosition)
let paths = await findPathsInner({ let paths = await findPaths({
sourceElementId, sourceElementPosition, sourceElementId, sourceElementPosition,
targetElementId, targetElementPosition, targetElementId, targetElementPosition,
links, nodes, direction, links, nodes,
maxLengthOfPath, pathSoFar: [] maxLengthOfPath, pathSoFar: []
}); });
console.log(`findDistance from ${sourceElementPosition} to ${targetElementPosition}`)
console.log(targetElementId)
console.log(direction)
console.log(paths)
let weights = [] let weights = []
for (let path of paths) { for (let path of paths) {
let currentSource = sourceElementId let currentSource = sourceElementId
@ -106,25 +165,26 @@ async function findDistance({
} }
weights.push(weight) 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
//return weights.map(weight => Math.round(weight*100)/100)
} }
async function findDistancesForAllElements({ nodes, links }) { 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 referenceElements = nodes.filter(x => x.isReferenceValue)
let midpoint = Math.round(nodes.length / 2) let midpoint = Math.round(nodes.length / 2)
let referenceElement = referenceElements.length > 0 ? referenceElements[0] : nodes[midpoint] let referenceElement = referenceElements.length > 0 ? referenceElements[0] : nodes[midpoint]
// console.log(nodes)
console.log(`referenceElement.position: ${referenceElement.position}`) console.log(`referenceElement.position: ${referenceElement.position}`)
/* Get distances. */
let distances = nodes.map(node => { let distances = nodes.map(node => {
if (node.isReferenceValue || (node.id == referenceElement.id)) { if (node.isReferenceValue || (node.id == referenceElement.id)) {
return [1] return [1]
} else { } else {
console.log("node") console.log("node")
console.log(node) console.log(node)
let isUpwardsDirection = referenceElement.position < node.position
let distance = findDistance({ let distance = findDistance({
sourceElementId: referenceElement.id, sourceElementId: referenceElement.id,
sourceElementPosition: referenceElement.position, sourceElementPosition: referenceElement.position,
@ -132,7 +192,6 @@ async function findDistancesForAllElements({ nodes, links }) {
targetElementPosition: node.position, targetElementPosition: node.position,
nodes: nodes, nodes: nodes,
links: links, links: links,
direction: isUpwardsDirection ? "upwards" : "downwards"
}) })
return distance return distance
} }
@ -141,23 +200,11 @@ async function findDistancesForAllElements({ nodes, links }) {
return distances 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 }) { 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) { if (isListOrdered && !(orderedList.length < listOfElements.length) && rows.length == 0) {
let nodes = [] let nodes = []
let positionDictionary = ({}) let positionDictionary = ({})
@ -165,8 +212,6 @@ export async function buildRows({ isListOrdered, orderedList, listOfElements, li
nodes.push({ ...listOfElements[id], position: pos }) nodes.push({ ...listOfElements[id], position: pos })
positionDictionary[id] = 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 => ({ links = links.map(link => ({
...link, ...link,
sourceElementPosition: positionDictionary[link.source], sourceElementPosition: positionDictionary[link.source],
@ -180,161 +225,63 @@ export async function buildRows({ isListOrdered, orderedList, listOfElements, li
name: element.name, name: element.name,
distances: distances[i] distances: distances[i]
})) }))
console.log("rows@CreateTableWithDistances")
console.log(rows) console.log(rows)
setTableRows(rows) setTableRows(rows)
} else {
// rows = []
// Do nothing
} }
// return rows
} }
export function CreateTableWithDistancesWithUseEffect({ isListOrdered, orderedList, listOfElements, links, tableRows, setTableRows }) {
useEffect(async () => { export function CreateTable({ tableRows }) {
await buildRows({ isListOrdered, orderedList, listOfElements, links, rows: tableRows, setTableRows }) /* This function receives a list of rows, and displays them nicely. */
/* function abridgeArrayAndDisplay(array) {
// https://stackoverflow.com/questions/57847626/using-async-await-inside-a-react-functional-component let newArray
// https://stackoverflow.com/questions/54936559/using-async-await-in-react-component let formatForDisplay
// https://reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects if (array.length > 10) {
if (isListOrdered && ! (orderedList.length < listOfElements.length) && rows.length == 0) { newArray = array.slice(0, 9)
let nodes = [] formatForDisplay = newArray.map(d => formatLargeOrSmall(d))
let positionDictionary=({}) formatForDisplay[9] = "..."
orderedList.forEach((id, pos) => { } else {
nodes.push({ ...listOfElements[id], position: pos }) newArray = array
positionDictionary[id] = pos formatForDisplay = newArray.map(d => formatLargeOrSmall(d))
})
// 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)
} }
*/ let result = JSON.stringify(formatForDisplay, null, 2).replaceAll(`"`, "")
}); // this useEffect should ideally only work when isListOrdered changes, but I haven't bothered to program that. return result
return (
<div>
<table className="">
<thead>
<tr >
<th >Id</th>
<th>&nbsp;&nbsp;&nbsp;</th>
<th >Position</th>
<th>&nbsp;&nbsp;&nbsp;</th>
<th >Element</th>
<th> &nbsp;&nbsp;&nbsp;</th>
<th >Possible relative values</th>
<th> &nbsp;&nbsp;&nbsp;</th>
<th >Average relative value</th>
</tr>
</thead>
<tbody>
{tableRows.map(row => <tr key={row.id}>
<td className="" >{row.id}</td>
<td>&nbsp;&nbsp;&nbsp;</td>
<td className="" >{row.position}</td>
<td>&nbsp;&nbsp;&nbsp;</td>
<td className="">{row.name}</td>
<td>&nbsp;&nbsp;&nbsp;</td>
<td className="">{abridgeArrayAndDisplay(row.distances)}</td>
<td>&nbsp;&nbsp;&nbsp;</td>
<td className="">{formatLargeOrSmall(avg(row.distances))}</td>
</tr>
)}
</tbody>
</table>
</div>
)
}
export function CreateTableWithDistances({ tableRows }) {
return (
<div>
<table className="">
<thead>
<tr >
<th >Id</th>
<th>&nbsp;&nbsp;&nbsp;</th>
<th >Position</th>
<th>&nbsp;&nbsp;&nbsp;</th>
<th >Element</th>
<th> &nbsp;&nbsp;&nbsp;</th>
<th >Possible relative values</th>
<th> &nbsp;&nbsp;&nbsp;</th>
<th >Average relative value</th>
</tr>
</thead>
<tbody>
{tableRows.map(row => <tr key={row.id}>
<td className="" >{row.id}</td>
<td>&nbsp;&nbsp;&nbsp;</td>
<td className="" >{row.position}</td>
<td>&nbsp;&nbsp;&nbsp;</td>
<td className="">{row.name}</td>
<td>&nbsp;&nbsp;&nbsp;</td>
<td className="">{abridgeArrayAndDisplay(row.distances)}</td>
<td>&nbsp;&nbsp;&nbsp;</td>
<td className="">{formatLargeOrSmall(avg(row.distances))}</td>
</tr>
)}
</tbody>
</table>
</div>
)
}
function CreateTableWithoutDistances({ isListOrdered, orderedList, listOfElements, links }) {
if (!isListOrdered || orderedList.length < listOfElements.length) {
return (<div>{""}</div>)
} 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 (
<div>
<table className="">
<thead >
<tr >
<th >Id</th>
<th>&nbsp;&nbsp;&nbsp;</th>
<th >Element</th>
<th> &nbsp;&nbsp;&nbsp;</th>
</tr>
</thead>
<tbody>
{rows.map(row => <tr key={row.id}>
<td className="" >{row.id}</td>
<td>&nbsp;&nbsp;&nbsp;</td>
<td className="">{row.name}</td>
</tr>
)}
{ }
</tbody>
</table>
</div>
)
} }
return (
<div>
<table className="">
<thead>
<tr >
<th >Id</th>
<th>&nbsp;&nbsp;&nbsp;</th>
<th >Position</th>
<th>&nbsp;&nbsp;&nbsp;</th>
<th >Element</th>
<th> &nbsp;&nbsp;&nbsp;</th>
<th >Possible relative values</th>
<th> &nbsp;&nbsp;&nbsp;</th>
<th >Average relative value</th>
</tr>
</thead>
<tbody>
{tableRows.map(row => <tr key={row.id}>
<td className="" >{row.id}</td>
<td>&nbsp;&nbsp;&nbsp;</td>
<td className="" >{row.position}</td>
<td>&nbsp;&nbsp;&nbsp;</td>
<td className="">{row.name}</td>
<td>&nbsp;&nbsp;&nbsp;</td>
<td className="">{abridgeArrayAndDisplay(row.distances)}</td>
<td>&nbsp;&nbsp;&nbsp;</td>
<td className="">{formatLargeOrSmall(avg(row.distances))}</td>
</tr>
)}
</tbody>
</table>
</div>
)
} }
export const CreateTable = CreateTableWithDistances // CreateTableWithoutDistances;
/* Testing */ /* Testing */
//import fs from 'fs'; //import fs from 'fs';
//import path from 'path'; //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 links = JSON.parse(fs.readFileSync(path.join(directory, 'distancesExample.json'), 'utf8'));
let nodes = JSON.parse(fs.readFileSync(path.join(directory, 'listOfPosts.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)) console.log(JSON.stringify(paths, null, 2))
*/ */
/* /*

View File

@ -1,6 +1,6 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import * as d3 from 'd3'; 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; let getlength = (number) => number.toString().length;

View File

@ -68,3 +68,5 @@ export function expectedNumMergeSortSteps(n) {
return Math.ceil((n ** 2) / (n + 2)) + expectedNumMergeSortSteps(Math.ceil(n / 2)) return Math.ceil((n ** 2) / (n + 2)) + expectedNumMergeSortSteps(Math.ceil(n / 2))
} }
} }
export const avg = arr => arr.reduce((a, b) => (a + b), 0) / arr.length