d3 js: creating a nested array (matrix) from a flat array - javascript

I have an array of objects nodes, where each component object has .id property, and I would like to create a square matrix which is a nested array indexed by [id][id] representing node-by-node interactions:
nodes.forEach(function(node, i) {
matrix[node.id] = nodes.forEach(
function(node1, j) {
console.log(i,j);
return {
"x": j,
"y": i,
"z": 0
};
});
console.log(i, matrix[node.id]);
});
In the console I am getting:
...
149 1
...
149 148
149 149
149 undefined
Why is the object is not assigned in the expression matrix[node.id] = ...? Why there is no error or warning? How can I fix it?
Upd: following #pilotcam explanation that forEach does not return a value, I tried following:
var matrix = [];
var testnodes = [{id: "aaa", a:10},
{id: "aab", a:20},
{id: "aac", a:30},
{id: "aba", a:40}]
testnodes.forEach(function(node, i) {
matrix[node.id] = [];
// [{x: 1, y:2, z:0}, {x:2,y:3,z:0}];
testnodes.forEach(
function(node1, j) {
matrix[node.id][node1.id] = {
x: j,
y: i,
z: 0
};
console.log(i,j, node.id, node1.id, matrix[node.id][node1.id]);
});
console.log(i, matrix[node.id]);
});
Still my matrix is not getting filled in the inner loop:
...
3 1 aba aab Object { x: 1, y: 3, z: 0 }
3 2 aba aac Object { x: 2, y: 3, z: 0 }
3 3 aba aba Object { x: 3, y: 3, z: 0 }
3 Array [ ]

The javascript forEach method does not return a value. You probably want to do
matrix[node.id] = [];
...and manipulate that inside the second forEach. From the question posed, I'm guessing you want something like this:
nodes.forEach(function(node, i) {
matrix[node.id] = [];
nodes.forEach(
function(node1, j) {
console.log(i,j);
matrix[node.id][node1.id] = {
"x": j,
"y": i,
"z": 0
};
});
console.log(i, matrix[node.id]);
});
I modified the fiddle to loop through your hash table and show that it is probably doing what you want. https://jsfiddle.net/rtxbzove/

The issue is that I try to index an array with non-integer values. The proper way seems to use an 'object' / hash table:
var matrix = {};
var testnodes = [{id: "aaa", a:10},
{id: "aab", a:20},
{id: "aac", a:30},
{id: "aba", a:40}]
// with simple for loops:
for (var i = 0, len = testnodes.length; i < len; i++) {
matrix[testnodes[i].id] = {};
for (var j = 0, len = testnodes.length; j < len; j++) {
matrix[testnodes[i].id][testnodes[j].id] = {
x: j,
y: i,
z: 0
};
}
console.log( "matrix:", matrix[testnodes[i].id] );
}
console.log( "matrix:", matrix);
Alternatively, with forEach loops:
testnodes.forEach(function(node, i) {
matrix[node.id] = {};
testnodes.forEach(
function(node1, j) {
console.log(i,j);
matrix[node.id][node1.id] = {
"x": j,
"y": i,
"z": 0
};
});
console.log(i, matrix[node.id]);
});

Related

aggregated sum based on keys in javascript

I have an array of object that looks like this
var data = [];
data[0] = {'TrueDestination': 'EU.UK.London', 'EU.UK.London':5, 'EU.UK.Bath':4, 'EU.France.Lyon':1, 'EU.France.Paris':0, 'Asia.Japan.Tokyo':4},
data[1] = {'TrueDestination': 'EU.UK.Bath', 'EU.UK.London':7, 'EU.UK.Bath':1, 'EU.France.Lyon':8, 'EU.France.Paris':0, 'Asia.Japan.Tokyo':1},
data[2] = {'TrueDestination': 'EU.France.Paris', 'EU.UK.London':2, 'EU.UK.Bath':2, 'EU.France.Lyon':2, 'EU.France.Paris':6, 'Asia.Japan.Tokyo':3},
data[3] = {'TrueDestination': 'EU.France.Lyon', 'EU.UK.London':9, 'EU.UK.Bath':0, 'EU.France.Lyon':1, 'EU.France.Paris':0, 'Asia.Japan.Tokyo':2},
data[4] = {'TrueDestination': 'EU.France.Lyon', 'EU.UK.London':2, 'EU.UK.Bath':4, 'EU.France.Lyon':3, 'EU.France.Paris':7, 'Asia.Japan.Tokyo':7},
I would like to
apply a function to the keys of my objects in my data so I can remove dot separated strings. So for example remove London or UK.London from EU.UK.London but not UK from UK.London or from EU.UK.London. (Added after a comment by T.J. Crowder below: to avoid confusion, you remove going backwards)
Then, for each object to take the aggregated sum grouped by the key
For instance if I strip the city, the last bit in the key strings, i want to end up with an array
var data = [];
data[0] = {'TrueDestination': 'EU.UK', 'EU.UK':9, 'EU.France':1, 'Asia.Japan':4},
data[1] = {'TrueDestination': 'EU.UK', 'EU.UK':8, 'EU.France':8, 'Asia.Japan':1},
data[2] = {'TrueDestination': 'EU.France', 'EU.UK':4, 'EU.France':8, 'Asia.Japan':3},
data[3] = {'TrueDestination': 'EU.France', 'EU.UK':9, 'EU.France':1, 'Asia.Japan':2},
data[4] = {'TrueDestination': 'EU.France', 'EU.UK':6, 'EU.France':10, 'Asia.Japan':7},
I have written this to strip the keys
function stripper(d, k){
// k controls how many times you want to strip the string
for (i=0; i<k; ++i){
if (d.lastIndexOf('.') > 0){
d = d.substring(0, d.lastIndexOf('.'))
}
}
return d
}
but my function to work on the keys is a real embarrassment. (and it doesnt work!) Anyways, this is where I am so far
stripLevel = 1
columnNames = data.columns.map(d => stripper(d, stripLevel))
for (j=0; j<columnNames.length; ++j){
cname = columnNames[j];
for (i=0; i<data.length; ++i) {
var _obj = {}
obj = data[i]
_keys = Object.keys(obj)
total = 0
for (k = 0; k < _keys.length; ++k) {
cur = _keys[k]
if ((cname != 'model_class') && (stripper(cur, stripLevel) === cname)) {
total += parseFloat(obj[cur])
}
_objTemp = {[stripper(cur, stripLevel)]: total}
Object.assign(_obj, _objTemp)
total = 0
}
}
}
Any help greatly appreciated
You could create new object and add the values of the same key.
It works with the entries of the object and the destructured key/value pair k/v and a function for getting the first parts of the dotted strings and a check of the key.
If the key k is equal to TrueDestination, then it takes the unchanged key and assigns the changed value to it. in all other cases, it takes the value of the property with the new key or zero, adds the value and assign the sum to the property.
Finally the object is returned.
var data = [{ TrueDestination: 'EU.UK.London', 'EU.UK.London': 5, 'EU.UK.Bath': 4, 'EU.France.Lyon': 1, 'EU.France.Paris': 0, 'Asia.Japan.Tokyo': 4 }, { TrueDestination: 'EU.UK.Bath', 'EU.UK.London': 7, 'EU.UK.Bath': 1, 'EU.France.Lyon': 8, 'EU.France.Paris': 0, 'Asia.Japan.Tokyo': 1 }, { TrueDestination: 'EU.France.Paris', 'EU.UK.London': 2, 'EU.UK.Bath': 2, 'EU.France.Lyon': 2, 'EU.France.Paris': 6, 'Asia.Japan.Tokyo': 3 }, { TrueDestination: 'EU.France.Lyon', 'EU.UK.London': 9, 'EU.UK.Bath': 0, 'EU.France.Lyon': 1, 'EU.France.Paris': 0, 'Asia.Japan.Tokyo': 2 }, { TrueDestination: 'EU.France.Lyon', 'EU.UK.London': 2, 'EU.UK.Bath': 4, 'EU.France.Lyon': 3, 'EU.France.Paris': 7, 'Asia.Japan.Tokyo': 7 }],
result = data.map(o => Object.entries(o).reduce((o, [k, v]) => {
const firsts = k => k.split('.').slice(0, -1).join('.');
if (k === 'TrueDestination') {
o[k] = firsts(v);
} else {
k = firsts(k);
o[k] = (o[k] || 0) + v;
}
return o;
}, {}));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Generate collision objects from multidimensional array for HTML5 tile map

I am creating an HTML5 platform game using objects for collision detection and using a 2d tile map to render the level. That is all working.
I want to use the same 2d array to build the object array dynamically to allow the player to build maps as required and also for ease of creating the maps in the first place. When hardcoding the object array, everything works so I know that the collision detect and game engine work.
While I can create objects for each individual array element, I am looking to build objects that have width based on the number of matching elements in the array, (each element is 25x25) i.e. if 3 array elements are 1,1,1 then the object will have a width of 75. Maybe some code will help explain:
The following tile array
var arr1 = [
[0,0,0,1,1,1,1,0,0,0],
[0,0,0,0,0,0,0,0,0,0],
[0,0,0,2,2,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0],
[3,3,3,0,0,0,0,0,0,0]
];
should produce the following object array:
[
{x: 75, y: 0, width: 100, height: 25, value: 1},
{x: 75, y: 50, width: 50, height: 25, value: 2},
{x: 0, y: 100, width: 75, height: 25, value: 3}
]
but it instead it is producing the following:
[
{x: 75, y: 0, width: 25, height: 25, value: 1},
{x: 100, y: 0, width: 25, height: 25, value: 1},
{x: 125, y: 0, width: 25, height: 25, value: 1}
]
My logic is obviously wrong but I can't for the life of me get it.
Example code is below:
Any help really appreciated:
var tmpObj = {};
var objArr = [];
var arr1 = [
[0,0,0,1,1,1,1,0,0,0],
[0,0,0,0,0,0,0,0,0,0],
[0,0,0,2,2,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0],
[3,3,3,0,0,0,0,0,0,0]
];
for (let i=0; i<arr1.length; i++) {
for (let j=0; j<arr1[i].length; j++) {
if (arr1[i][j] > 0 && arr1[i][j] < 6) { // platform blocks only 1 - 5
if (tmpObj.x === undefined) {
tmpObj = {
x: j * 25,
y: i * 25,
width: 25,
height: 25,
value: arr1[i][j]
}
} else if (arr1[i][j] == arr1[i][j-1] && arr1[i][j] == tmpObj.v) {
tmpObj.w += 25;
} else if (arr1[i][j] !== tmpObj.v) { // new tile type
objArr.push(tmpObj);
tmpObj = {
x: j * 25,
y: i * 25,
width: 25,
height: 25,
value: arr1[i][j]
}
} else {
objArr.push(tmpObj);
tmpObj = {};
}
}
}
}
console.log(objArr);
Looking at what you are trying to do your implementation is way too complicated. Rather than hunt down the bug (for which I would have used devTools and stepped through the code line by line to find where the problem was.) I rewrote the function using a while loop to find the width of joined tiles.
I took liberty with the object property names but I am sure you can change it to your needs.
const objArr = [];
const arr1 = [
[0,0,0,1,1,1,1,0,1,0],
[2,0,0,0,0,0,0,0,0,3],
[0,0,0,4,4,0,4,4,4,4],
[0,0,0,0,0,0,0,0,0,0],
[3,3,3,5,5,4,0,0,0,0]
];
const tileSize = 25;
for (let i = 0; i < arr1.length; i++) {
const row = arr1[i]
for (let j = 0; j < row.length; j++) {
if (row[j] > 0 && row[j] < 6) {
let count = j + 1;
while (count < row.length && row[count] === row[j]) { count += 1 }
objArr.push({
x: j * tileSize,
y: i * tileSize,
w: tileSize * (count - j),
h: tileSize,
tile: row[j]
});
j = count - 1;
}
}
}
// show results
objArr.forEach(log);
// unrelated to answer. (I hate the console SO has for snippets.)
function log(data){
show.appendChild(
Object.assign(
document.createElement("div"), {
textContent :
Object.keys(data).reduce((str, key) => {
return str + (" " + key+ ": " + data[key]).padEnd(8,".");
}, ""
)
}
)
);
}
<code id="show"></code>

How to create multidimensional array for each row?

For a website I used a grid layout. What I want is to store all items per row inside a row.
I have an overall array that is calling arrWrap = [];. Now I want to create for each row an new array, where I store each time 4 items. So a new array should be created after the third item in a row.
How do I achieve this?
I use Javascript for this project.
var arrPos = [];
for (var i = 0; i < elements.length; ++i) {
arrPos[i] = i;
console.dir(arrPos[i]);
if (arrPos[i] > 3) {
alert(arrPos[i]);
};
}
var arrWrap = [];
var steps = 4;
for (var i = 0; i < elements.length; i=i+steps) {
arrWrap.push(elements.slice(i,i+steps));
}
This proposal feature the Array.prototype.reduce and offers two solutions:
Grouped by consecutive elements dataGroupedA
[
[ 0, 1, 2 ],
[ 3, 4, 5 ],
[ 6, 7, 8 ],
[ 9, 10, 11 ],
[ 12, 13, 14 ]
]
Grouped by the 5th element dataGroupedB
[
[ 0, 5, 10 ],
[ 1, 6, 11 ],
[ 2, 7, 12 ],
[ 3, 8, 13 ],
[ 4, 9, 14 ]
]
The calculation of index is the important part. The rest is standard default assignment and pushing the actual element.
var data = Array.apply(Array, { length: 15 }).map(function (_, i) { return i; }),
dataGroupedA = data.reduce(function (r, a, i) {
var index = i / 3 | 0;
r[index] = r[index] || [];
r[index].push(a);
return r;
}, []),
dataGroupedB = data.reduce(function (r, a, i) {
var index = i % 5;
r[index] = r[index] || [];
r[index].push(a);
return r;
}, []);
document.write('<pre>' + JSON.stringify(data, 0, 4) + '</pre>');
document.write('<pre>' + JSON.stringify(dataGroupedA, 0, 4) + '</pre>');
document.write('<pre>' + JSON.stringify(dataGroupedB, 0, 4) + '</pre>');
Please use the following code:
var cIndex= 0;
var data=[];
var cars = ["Saab", "Volvo", "BMW", "a", "v", "c", "q"];
for(var i = 0; i <= 3; i++)
{
cIndex = cIndex + 3;
var row = cars.slice(cIndex -3,cIndex );
data.push(row);
}
console.log(data);

Array of objects to line chart series? (+ for Crossfilter solution)

In JavaScript, how do I transform a "long table format" to a data structure suitable for a line chart:
var data = [
{"type":"A"," year":2000," value":50},
{"type":"A"," year":2001," value":51},
{"type":"A"," year":2002," value":52},
{"type":"B"," year":2000," value":60},
{"type":"B"," year":2001," value":55},
{"type":"B"," year":2002," value":57}
]
=>
var series = [
{type: "A", values : [{x: 2000, y: 50}, {x: 2001, y: 52},] },
{type: "B", values : [{x: 2000, y: 60}, {x: 2001, y: 55},] },
]
A vanilla JavaScript solution as well as a solution with the Crossfilter library - that can work on any dimension of the data - would be valuable:
https://github.com/square/crossfilter/wiki/API-Reference
Good question, but not staightforward ; )
Have a look at https://github.com/NickQiZhu/dc.js/pull/91
you'll have something like :
cr = crossfilter(data);
typeDim=cr.dimension(function(d) {return d.type});
typeGroup=typeDim.group();
yearDim=cr.dimension(function(d) {return d.year});
valDim=cr.dimension(function(d) {return d.value});
yearTypeGroup = dateDimension.group();
yearTypeGroup.reduce(
// add
// pivot[0].idx()[i] returns the index of statusGroup
// the reduce function hence construct a key-value object, keys being indexes of the pivot group.
function(p, v, i, pivot) {
++p[pivot[0].idx()[i]].count;
p[pivot[0].idx()[i]].value += +v.value;
return p;
},
//remove
function(p, v,i,pivot) {
--p[pivot[0].idx()[i]].count;
p[pivot[0].idx()[i]].value -= +v.value;
return p;
},
//init
function(pivot) {
var l, i, obj ={};
if(l = pivot[0].all().length){
for(i=0; i<l; ++i) {
obj[i] = {count:0, value:0}
}
}
return obj
}
);
yearTypeGroup.pivot(typeGroup)
Good luck ; )
C.

Javascript, repeating an object key N-times, being N its value

I was wondering how to do this in the more cleaner and optimal way:
I have an Object with the following structure:
{
"125": 2,
"439": 3,
"560": 1,
"999": 2,
...
}
I want to create a flat array repeating every key, the number of times indicated by its value. And bonus points for converting keys (strings) to integers. In this example, the resulting array should be:
[ 125, 125, 439, 439, 439, 560, 999, 999 ]
I've tried several ways but they all look over-engineered. For sure there is an easier way.
This is what I've got with underscore (and it returns an Array of strings, nor integers):
_.compact(_.flatten(_.map(files, function(num, id) {
return new Array(num+1).join('$'+id).split('$')
})))
I know there are plenty of ways to accomplish this. I just only want a clean and quick way. Being a Ruby developer it could be as easy as:
> files = {"125" => 2, "439" => 3, "560" => 1, "999" => 2}
=> {"125"=>2, "439"=>3, "560"=>1, "999"=>2}
> files.map {|key, value| [key.to_i] * value}.flatten
=> [125, 125, 439, 439, 439, 560, 999, 999]
Thanks in advance.
Try this:
var obj = {
"125": 2,
"439": 3,
"560": 1,
"999": 2
}
var arr = [];
for (prop in obj) {
for (var i = 0; i < obj[prop]; i++)
arr.push(parseInt(prop));
}
console.log(arr)
I know this is plain JavaScript but seems cleaner to me than the code you posted:
var dict = {
"125": 2,
"439": 3,
"560": 1,
"999": 2
}
var result = [];
for(key in dict)
for(i = 0; i < dict[key]; i++)
result.push(key * 1);
alert(result);
Hmm... not sure if I got at, but maybe something like this:
var myObj = {
"125": 2,
"439": 3,
"560": 1,
"999": 2
},
myArray = [];
for(k in myObj){
for(i = 0; i < myObj[k]; i++){
myArray.push(k);
}
}
console.log(myArray)
The problem with the other answers above is that the for..in language construct in javascript is going to involve all keys from the objects prototype chain. In this case, we should check and add only the correct keys.
var obj= {
"125": 2,
"439": 3,
"560": 1,
"999": 2
}
var arr=[];
for (var item in map) {
//important check!
if (map.hasOwnProperty(item)) {
arr.push(item);
}
}
Also see: http://www.yuiblog.com/blog/2006/09/26/for-in-intrigue/
Whether any of these approaches is cleaner is quite subjective:
// some helper function for creating an array with repeated values
function repeat(val, times) {
var arr = [];
for(var i = 0; i < times; i = arr.push(val));
return arr;
}
function convert(obj) {
var result = [], key;
for(key in obj) {
result = result.concat(repeat(+key, obj[key]));
}
return result;
}
Or a more functional approach:
Object.keys(obj).reduce(function(result, key) {
return result.concat(repeat(+key, obj[key]));
}, []);
// with underscore.js
_.reduce(_.keys(obj), function(result, key) {
return result.concat(repeat(+key, obj[key]));
}, []);
A helper function:
function flatten(obj){
//static Array method: create array (a elements, value b)
Array.aXb = Array.aXb || function(a,b){
b = b || 0;
return String(this(a)).split(',').map(function(){return b;});
}
//obj2array
var arr = [];
for (var k in obj)
if (+obj[k]) arr = arr.concat(Array.aXb(+obj[k],k));
return arr;
}
var obj= {"125": 2,"439": 3,
"560": 1,"999": 2 },
flatten(obj); //=> [125,125,439,439,439,560,999,999]

Categories

Resources