weighted graph implement using js - javascript

I created this graph and am trying to make it wieghted directed graph
class Graph{
#nodes;
constructor(){
this.#nodes={}
}
addNode(node){
this.#nodes[node]=[]
}
addEdge(source,vertix){
if( ! this.#nodes[source] || ! this.#nodes[vertix]){
return false
}
// this.vertix[destination]=distancex
if(! this.#nodes[source].includes(vertix)){
this.#nodes[source].push(vertix)
}
}
showNodes(){
console.log(this.#nodes)
}
}
and now am trying to add edges :
for(let i=0;i<citiesnamesarr.length;i++)
{
mapgraph.addNode(citiesnamesarr[i])
var x={}
var citiesform=document.getElementsByClassName(`check${citiesnamesarr[i]} `)
var distanceform=document.getElementsByClassName(`distance${citiesnamesarr[i]} `)
for(let j=0;j<citiesform.length;j++)
{
var edge=citiesform[j].value
var distance=distanceform[j].value
x[edge]=distance
}
v[i]=x
mapgraph.addEdge(citiesnamesarr[i],v[i])
}
but when I print the graph it gives me an empty array :
{city1: Array(0), city2: Array(0), city3: Array(0)}
knowing when I tried to print the array v it works
0:{city2: '87', city1: ''}
1: {city0: '12', city1: '78'}
2: {city0: '', city1: '21'}

The main problem in what you're doing is that your method addEdge is meant to add one edge at the time, but when you're calling it in your code, you're trying to add multiple edges at the same time.
I rewrote your code for better understanding:
for(let cityname of citiesnamesarr){
mapgraph.addNode(cityname);
var adjacentVertices={};
var cityInputArr=document.getElementsByClassName(`check${cityName} `);
var distanceInputArr=document.getElementsByClassName(`distance${cityName} `);
for(let key in cityInputArr){
var destination=cityInputArr[key].value;
var distance=distanceInputArr[key].value;
adjacentVertices[destination]=distance;
}
mapgraph.addEdge(cityname,adjacentVertices);
}
To solve the issues, you'd either need to call your method addEdges and do something like:
addEdges(node,edges){
this.#nodes[node] = edges;
}
And call this method instead.
Or you can keep a method that adds a single edge with:
addEdge(source, destination, distance){
this.#nodes[source].push([destination, distance]);
}
And change your code to something like:
for(let cityname of citiesnamesarr){
mapgraph.addNode(cityname);
var cityInputArr=document.getElementsByClassName(`check${cityName} `);
var distanceInputArr=document.getElementsByClassName(`distance${cityName} `);
for(let key in cityInputArr){
var destination=cityInputArr[key].value;
var distance=distanceInputArr[key].value;
mapgraph.addEdge(cityname,destination, distance);
}
}
Note that a weighed graph has a more complex structure that an unweighed graph.
An unweighed graph can be represented as an adjacency list:
0: [1, 4, 6]
1: [0, 3, 4]
etc.
While for a weighed graph needs to store an additional value:
0: [[1, 300], [4, 250], [6, -20]]
1: [[0, 100], [3, 76], [4, -10]]
etc.
Depending on which algorithms you'd like to use, a matrix might be more convenient.

Related

Generate many null values

May I know how I add many null values when data gets generated using Math.random()
500 points are getting generated. I want like 250 to 350 points to be null. Below fiddle has demo that generates random data. But I want to add many null values to it. Thank you.
https://jsfiddle.net/kf9nr0v5/15/
Assigning manually like below is time consuming.:
data[100] = [100, null];
data[101] = [101, null];
data[102] = [102, null];
data[103] = [103, null];
Tried this: works well as per suggestion.
for (var i=20; i<=480; i+=3)
{
data[i] = [i, null];
}

How filter using crossfilter in D3

I'm trying to filter my dataset using crossfilter but with no luck (the full result is returned in my filterType var). My code appears to be inline with similar examples online but I must be missing something obvious. The data is for my newborn's sleep patterns, hence this may be contributing to my demise.
Can anyone enlighten me on my oversight?
//Load sample sleep data
var dataset = [
{day: 1, type: 'day', totalSleep: 8},
{day: 1, type: 'night', totalSleep: 7},
{day: 2, type: 'day',totalSleep: 8},
{day: 2, type: 'night', totalSleep: 7}
];
//Crossfilter data
var cf = crossfilter(dataset);
//Create type dimension
var typeDim = cf.dimension(function(d) {return d.type});
//Reduce type by sleep
var sleepByType = typeDim.group().reduceSum(item => item.totalSleep);
//Display result
var allTypes = sleepByType.all();
console.log('All types:');
console.log(allTypes);
//Filter on type
typeDim.filter('day');
//Filtered result
var filterTypes = sleepByType.all();
console.log('Filtered types:');
console.log(filterTypes);
By design, a group does not observe its own dimension's filters.
This is because you usually don't want a chart to filter out its own data. For example, except in rare cases, if you brush an area of the x axis of a line chart, you wouldn't want the line outside the brush to fall to zero.
If you create another dimension on the same field, and filter that, you will see an effect on sleepByType:
var typeDim2 = cf.dimension(function(d) {return d.type});
typeDim2.filter('day');

Create convex hull with array of points in opencv.js

Im trying to create a convex hull with opencv.js based on an array with points, does anyone know a way to do this correctly and efficient? An array would look like this:
[
[5,5],
[10,10],
[15,15]
...
]
-> where the first value would be the x and the second the y value, but it wouldn't be a problem to change this format to something more suitable.
Thnx for the help :)
As far I could experiment OpenCV stores contour/hull data in Mat format with type CV_32SC2: essentially a flat list of 32bit short integers in [x1,y1,x2,y2,x3,y3,...] order.
Note the two channels/planes part of 32SC2: one channel for all the x values and another for all the y values
You can manually create such a Mat, access it's data32S property and fill in each value:
let testHull = cv.Mat.ones(4, 1, cv.CV_32SC2);
testHull.data32S[0] = 100;
testHull.data32S[1] = 100;
testHull.data32S[2] = 200;
testHull.data32S[3] = 100;
testHull.data32S[4] = 200;
testHull.data32S[5] = 200;
testHull.data32S[6] = 100;
testHull.data32S[7] = 200;
However OpenCV.js comes with a handy method to convert a flat array of values to such a Mat:
let testHull = cv.matFromArray(4, 1, cv.CV_32SC2, [100,100,200,100,200,200,100,200])
If your array is nested, you can simply use JS Array's flat() method to flatten it from a 2D array([[x1,y1]...]) to a 1D array ([x1,y1,...]).
So you don't have to worry about the Mat type and all that you can wrap it all into a nice function, for example:
function nestedPointsArrayToMat(points){
return cv.matFromArray(points.length, 1, cv.CV_32SC2, points.flat());
}
Here's a quick demo:
function onOpenCvReady(){
cv.then(test);
}
function nestedPointsArrayToMat(points){
return cv.matFromArray(points.length, 1, cv.CV_32SC2, points.flat());
}
function test(cv){
console.log("cv loaded");
// make a Mat to draw into
let mainMat = cv.Mat.zeros(30, 30, cv.CV_8UC3);
// make a fake hull
let points = [
[ 5, 5],
[25, 5],
[25,25],
[ 5,25]
]
let hull = nestedPointsArrayToMat(points);
console.log("hull data", hull.data32S);
// make a fake hulls vector
let hulls = new cv.MatVector();
// add the recently created hull
hulls.push_back(hull);
// test drawing it
cv.drawContours(mainMat, hulls, 0, [192,64,0,0], -1, 8);
// output to canvas
cv.imshow('canvasOutput', mainMat);
}
<script async src="https://docs.opencv.org/4.4.0/opencv.js" onload="onOpenCvReady();" type="text/javascript"></script>
<canvas id="canvasOutput" width="30" height="30"></canvas>
Note that the above is a rough example, there's no data validation or any other fancier checks, but hopefully it illustrates the idea so it can be extended robustly as required.
Lets say that your points represent a contour:
var contours = new cv.MatVector();
for (var i = 0; i < points.size(); ++i) {
contours.push_back(new cv.Mat(points[i][0], points[i][1])
}
Now following this tutorial from opencv website:
// approximates each contour to convex hull
for (var i = 0; i < contours.size(); ++i) {
var tmp = new cv.Mat();
var cnt = contours.get(i);
// You can try more different parameters
cv.convexHull(cnt, tmp, false, true);
hull.push_back(tmp);
cnt.delete(); tmp.delete();
}

Modify multiple array elements simultaneously javascript

I'm making a simple rogue like game in JavaScript. I can generate a map procedurally without any issue, however I'm looking for a more ergonomic way of manually populating a map array. Here's an example of what I'm talking about.
This works.
//creating empty map array
city = new Array(1500);
//creating tile formats
tile1 = {walk: true, ...etc};
tile2 = {walk: false, ...etc};
//manually modifying array.
city[0] = tile1;
city[1] = tile1;
city[2] = tile1;
However, since some of these maps will be rather large, I'd like to be able to modify multiple elements all at once. The following doesn't work, but expresses what I'd like to do.
city[0,1,2,3,7,8,9,10] = tile1;
city[4,5,6,11,12,13] = tile2;
I tried quite a few different methods, but wasn't successful with any of them. I can't use a for statement without using math more complicated than it'd be worth since I'm using a single array to represent 2d space, and the tiles are not sequential.
Any suggestions?
Use forEach with ES6 arrow function in latest browsers
//creating empty map array
city = new Array(1500);
//creating tile formats
tile1 = {
walk: true
};
tile2 = {
walk: false
};
[0, 1, 2, 3, 7, 8, 9, 10].forEach(v => city[v] = tile1);
// older browser use [0, 1, 2, 3, 7, 8, 9, 10].forEach(function(v){ city[v] = tile1; });
[4, 5, 6, 11, 12, 13].forEach(v => city[v] = tile2);
console.log(city);
For older browser check polfill option of forEach method.

Format of the Path input in the Javascript Clipper

I'm using a JS Library called Javascript Clipper for polygon operation. As stated from the manual, the coordinate format of an input path is like the follows,
var paths = [[{X:30,Y:30},{X:130,Y:30},{X:130,Y:130},{X:30,Y:130}],
[{X:60,Y:60},{X:60,Y:100},{X:100,Y:100},{X:100,Y:60}]];
My question is, how to convert a regular JS array, say
var x = [40, 100, 1, 5, 25, 10] and var y= [22, 32, 11, 45, 75, 19] to the required format shown above? The actual case is, these coordinate points will not be typed manually, but obtained from another function, the output of which is not in the format required by the Javascript Clipper Library.
Something like this:
function makePath(xVals, yVals) {
var pathArray = [];
xVals.forEach(function(xVal, index) {
var yVal = yVals[index];
var coordObj = {xVal, yVal};
pathArray.push(coordObj);
})
return pathArray;
}
You can pass it your arrays, x and y, as makePath(x,y) to get the combined array out.
My method assumes that the lengths of the arrays x and y are the same.

Categories