Why do numbers appear at the end of my multidimensional array? - javascript

I'm a beginner at programming and looking for some help with multidimensional arrays (thank you in advance for your help!) I'm trying to complete a Kata on Code Wars, specifically "xD-Arrays for dummies." The prompt wants the user to return a multidimensional array based on an unknown set of arguments.
For example, the function dim( 3,3,"x" ), should return, [['x','x','x'],['x','x','x'],['x','x','x']]. First, creating a single array with 3 'x' (i.e. [x, x, x]), and then 3 more arrays to store the first array (i.e. [[x, x, x], [x, x, x], [x, x, x]]). To pass the kata you will be passed an unknown amount of arguments.
I've tried creating an iterative function, and settled on recursion, since we don't know the amount of arguments we might receive.
This is my code thus far...
function dim() {
// Create an array from the arguments given when the function is called
var args = Array.from(arguments);
// Store the first element as value
var val = args.pop();
// Store the second element which we will repeat for our first array
var d1 = args.pop();
var xDarr = [];
for (var x = 0; x < d1; x++) {
if (typeof val == 'function') {
xDarr.push(val());
} else {
xDarr.push(val);
};
};
if (args.length >= 1) {
var reversedArg = args.reverse();
return getMatrix(reversedArg, xDarr);
} else {
// if we are only passed 2 arguments, such as (dim (2, true)), return xDarr
return xDarr;
};
};
function getMatrix(arr, item) {
// dimArr equals the first array we have created, example: "[x, x, x]"
var dimArr = item;
for (var i = 0; i < arr.length; i++) {
// iterate through every other element in our args
var finalMatrix = [];
genMatrix(arr[i]);
dimArr = finalMatrix;
// here, the recursion function to return finalMatrix
function genMatrix(num) {
if (num === 1) {
return dimArr;
} else {
return finalMatrix.push(dimArr, genMatrix(num - 1))
};
};
console.log("Updated dimArr: " + dimArr)
};
return dimArr;
};
The function seems to work with arguments that are four elements long, such as dim( 2,2,2,0 ), but for odd arguments, such as dim( 3,3,"x" ), my function returns number(s) at the end of my array. dim( 3,3,"x" ) produces the following result: [ [ 'x', 'x', 'x' ], [ 'x', 'x', 'x' ], [ 'x', 'x', 'x' ], 2 ]. I've tried researching using Mozilla but can't figure out what that "2" at the end is. Thank you so much for your help!

The number is inserted by the recursive call in the function getMatrix:
return finalMatrix.push(dimArr, genMatrix(num - 1))
The return value here is what push returns, and push returns the new length of the finalMatrix array. This means that if the recursive call also gets to execute this line, you actually pass a number as second argument to push. And push will push whatever arguments you pass to it, hence the number that is pushed here.
You can solve this in different ways, but since the initial call of genMatrix doesn't actually use the returned value, you could just avoid returning anything, and just perform the recursion and push separately:
function genMatrix(num) {
if (num) {
finalMatrix.push(dimArr);
genMatrix(num - 1);
};
};
Note that there are simpler ways to generate a matrix. You could omit this function all together and in getMatrix do:
for (var i = 0; i < arr.length; i++) {
dimArr = Array(arr[i]).fill(dimArr);
}

The function should only pass three parameters no more -- no less:
function genTable(rows, cells, content) {...
rows: Number of rows
cells: Number of cells per row (also can be considered as columns)
content: A value to be placed at every cell
It takes two for loops to generate a 2D array (aka table and matrix)
An empty array is declared which represents the table -- the outer array that contains all of the sub-arrays.
|🡄 table array 🡆|
[ [...], [...], [...], [...] ]
The outer loop creates an empty array which represents a row -- the sub-arrays within the outer array (table)
[ [...], [...], [...], [...] ]
row row row row
The inner loop pushes the content [3rd parameter] into the array.
[ [content, content, content],
[content, content, content], [content, content, content], [content, content, content] ]
Once inner loop iterates Nc times (Nc = cells [2nd parameter]) the next loop of the outer loop pushes the full array into the table array then creates the next array (row).
This process is repeated Nr times (Nr = rows [1st parameter])
Once the outer loop is complete the table array is returned.
function genTable(rows, columns, content) {
const table = [];
for (let r = 0; r < rows; r++) {
let rowArray = [];
for (let c = 0; c < columns; c++) {
rowArray.push(content);
}
table.push(rowArray);
}
return table;
}
console.log(JSON.stringify(genTable(4, 6, 'X')));
console.log(JSON.stringify(genTable(8, 2, 'Y')));
console.log(JSON.stringify(genTable(2, 10, 'Z')));

Related

JavaScript : Pass-by-reference issues when creating an unknown dimension multi dimensional array function

I'm posting this question because I am trying to make a function that allows someone to create a multi-dim array. So, the user inputs an array of numbers which are the dimensions of the array (e.g entering [2, 4, 3] would output a 2x4x3 multi-dim array)
I have spent a couple of hours trying to imagine an algorithm that can do this in JS and I came up with this:
Note: I use Node.js v9.11.1
function generate(dimensions) {
// SA = sub-array (I will use this several times here)
// This array will store every SAs of the multi-dim array
// E.g for a 2x4x3 array, it will store a 2-item array, a 4-item array and a 3-item array
var arrays = []
// This fills `arrays` with the SAs
for (var i = 0; i < dimensions.length; i++) arrays.push(new Array(dimensions[i]).slice(0))
// Here it gets a bit complex (at least for me!)
// So what we do is that for each SA (except last), we fill it with copies of the current+1 SA
// So the SA at index 1 will be filled with copies of the array at index 2
// And the array at index 0 will be filled with arrays of index 1 (which was already filled because our for loop starts from the end)
// The array at index 0 is our final multi-dim array
// Goes from the before last SA to the first
for (var current = dimensions.length-2; current !== -1; current--) {
// Fills the current SA with index+1 SA
for (var i = 0; i < arrays[current].length; i++) arrays[current][i] = arrays[current+1].slice(0)
}
// Returns first array, the complete one
return arrays[0].slice(0)
}
My problem is that even if the array is well generated, some SA are passed by reference and not by value so when I do
my_array = generate([2, 4, 3])
my_array[1][2][1] = "hi!" // Fill a random place with "hi!"
Then when I do console.log(my_array), some other cases of the multi-dim array are filled with the same value.
This means that somewhere, an array is passed by reference rather than passed by value which is strange
because I checked the code multiple times and I don't find where this could come from (I use the Array.slice()
method to "copy" the array)
Have I missed something huge?
Your help would be rather appreciated!
To be honest, not sure how your trying to create your mult-dim array,..
But the first thing that springs to mind when seeing something like this, is recursion.
eg..
function generate(dimensions) {
if (!dimensions.length) throw new Error("no dims?");
const dimsize = dimensions[0];
if (dimensions.length === 1) {
return new Array(dimsize).fill(null);
}
const ret = [];
const subdims = dimensions.slice(1);
for (let l = 0; l < dimsize; l+= 1)
ret.push(generate(subdims));
return ret;
}
const my_array = generate([2, 4, 3])
my_array[1][2][1] = "hi!"
console.log(my_array);
I come up with this:
function generate(dims) {
if(dims.length > 0) {
let array = new Array(dims[0]).fill(0);
let childDims = dims.slice();
childDims.shift();
return array.map((el) => {
return generate(childDims);
});
} else return 0;
}
let foo = generate([2, 3, 2]);
foo[0][0][1] = 'hmmmm';
console.log(foo);
Also using recursion to create multidimensional array. But when creating arrays as You saw, have to be carefull about not passing references but real copies of arrays. Slice() will give You only shallow copy.

How to add elements to a list within a dict in javascript?

I am using a kind of dict in javascript and want to add an element to a list which is part of a kind of dictionary.
Here is the code snippet:
lines = [
[1,2],
[2,4],
[2,3],
[3,5]
];
nodes = [1,2,3,5,4];
function get_adjdict(nodes, lines) {
// create an empty something
adjacent = [];
// loop over all elements on the array 'nodes'. The variable 'node' is supposed to take the various values of the elements in 'nodes'. So in this example this will be the values 1,2,3,5,4.
for (var node in nodes) {
// Add a key-value pair to the object/array/whatever named 'adjacent'. key is the value of 'node, the value is an empty array.
adjacent.push({node:[]});
// loop over all elements on the array 'lines'. The variable 'line' is supposed to take the various values of the elements in 'lines'. So in this example this will be the values [1,2], then [2,4] and so on
for (var line in lines) {
// checks if the value of 'node' is present in the array 'line'
if (line.includes(node)) {
// If the first element of the array 'line' has the same value as 'node'...
if (line[0] == node) {
// ... add the second element of 'line' to 'adjacent[node]'
adjacent[node].push(line[1]) //ERROR
} else {
// ... add the first element of 'line' to 'adjacent[node]'
adjacent[node].push(line[0])
}
}
}
}
return adjacent
}
The error is "TypeError: adjacent[node].push is not a function". How to do it then?
Expected data-structure:
adjdict = {
1: [2],
2: [1,4,3],
3: [2,5],
4: [2],
5: [3]
}
This what you are looking for:
var lines = [
[1,2],
[2,4],
[2,3],
[3,5]
];
var nodes = [1,2,3,4,5];
function get_adjdict (nodes, lines) {
var adjacent = {};
var node, line;
for (var node_idx in nodes) {
node = nodes[node_idx];
adjacent[node] = [];
for (var line_idx in lines) {
line = lines[line_idx];
if (line.includes(node)) {
if (line[0] == node) {
adjacent[node].push(line[1]);
} else {
adjacent[node].push(line[0]);
}
}
}
}
return adjacent;
}
get_adjdict(nodes, lines);
Bear in mind that, when using the construction for (var idx in arr) {} in JavaScript, idx is the key in the iteration, not the value.
for (var node in nodes) {
In the above code, node takes values 0 to 4. nodes[node] would take values 1 to 5 as I think you are expecting.
I always use the suffix _idx for this kind of variables. In this case, rename node to node_idx or node_index and you will see how everything falls into place.
You could just iterate the lines and create the object.
function add(o, k, v) {
if (!o[k]) {
o[k] = [];
}
if (o[k].indexOf(v) === -1) {
o[k].push(v);
}
}
var lines = [[1, 2], [2, 4], [2, 3], [3, 5]],
result = {};
lines.forEach(function (l) {
add(result, l[0], l[1]);
add(result, l[1], l[0]);
});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can simplify it with first mapping all of the indexes first and than use a simple reduce to build the object.
const lines = [
[1,2],
[2,4],
[2,3],
[3,5]
];
const nodes = [1,2,3,4,5];
// Make a lookup table where all the numbers appear
var lookup = lines.slice(0).reduce( (o, node, i) => {
o[node[0]] = o[node[0]] || []; // if we have not found it, set an array
o[node[0]].push(node[1]); // add index value to the array
o[node[1]] = o[node[1]] || []; // if we have not found it, set an array
o[node[1]].push(node[0]); // add index value to the array
return o //return object for reduce
}, {})
var result = nodes.reduce( (o, n) => { //now loop over the nodes and make the `hash` table
o[n] = lookup[n] || []
return o
}, {})
console.log(result)
Don't use for/in loops on arrays. They can wind up iterating inherited properties as well as array items. for/in is for object iteration. Use .forEach() instead which will make working with the enumerated items much simpler (no indexes to manage).
Next, you are indicating that you want an object outputted, but you are creating adjacent as an array. Arrays inherit from objects, but they store their data differently.
Also, remember to formally declare your variables, otherwise they become global.
Lastly, don't rely on automatic semi-colon insertion. That can lead to bugs in certain edge cases.
If you follow good coding best practices, a lot of these kinds of issues just go away.
// Don't forget to use "var" to declare your variables otherwise
// they will become global.
var nodes = [1,2,3,4,5];
var lines = [
[1,2],
[2,4],
[2,3],
[3,5]
];
function get_adjdict(nodes, lines) {
var adjacent = {}; // <-- You are asking for an object, not an array
// Loop through the nodes array (use .forEach() to loop arrays)
nodes.forEach(function(node){
// Array to store results in
var results = [];
// Loop through the lines
lines.forEach(function(line) {
if (line.includes(node)) {
if (line[0] === node) {
results.push(line[1]);
} else {
results.push(line[0]);
}
// Set array as new property value
adjacent[node] = results;
}
});
});
// This needs to be outside of all loops, just before function terminates
return adjacent;
}
console.log(get_adjdict(nodes, lines));

How to create sublists from a single list in JavaScript using a delimiter

Essentially I want to port the solution for: Python spliting a list based on a delimiter word to JavaScript.
Given: var example = ['A', 'WORD', 'B' , 'C' , 'WORD' , 'D'];
If a delimiter of WORD is provided I would like to generate:
var result = [['A'], ['WORD','B','C'],['WORD','D']];
Is there an alternative to looping through the list to do this?
The best approach here is to first write down the algorithm, without getting into specific code. Sometimes this is called pseudo-code. Have you tried writing some? Here's an example:
Start off with an empty result of the form [[]]. The inner array we will call subarray.
Look at the next word in the input. If it's 'WORD', then add a new subarray to the result and make it the current subarray.
Add the word to the current subarray.
Repeat until input is empty.
This type of algorithm, where we are looping over an array, and building up some kind of result, is exactly what reduce was designed for. We can transform the pseudo-code above almost directly into JS as follows:
function split(array) {
var subarray = []; // subarray we are adding elts to
return array.reduce( // loop over the array
function(result, elt) { // and for each element
if (elt === 'WORD') // if it is word, then...
result.push(subarray = []); // start a new subarray and add to result
subarray.push(elt); // add the current element to the subarray
return result; // and return the updated result
},
[subarray]); // start off with an array with a single subarray
}
Using generators
If you are working in an ES6 environment, you could use ES6 generators:
function* group(array) {
var subarray = [];
for (var elt of array) {
if (elt === 'WORD') {
yield subarray;
subarray = [];
}
subarray.push(elt);
}
yield subarray;
}
Here, array can actually be any iterable, since we are using for..of to get its values.
Now you can print out your subarrays by
for (grp of group(example)) console.log(grp);
Or create an array of the groups:
Array.from(group(examples))
Is there an alternative to looping through the list to do this?
Someone is going to have to loop, be it you or some library routine. In the first case, it is reduce doing the looping; in the ES6 code, it is the generator.
this should do what you ask
var tree = function (seq, breaks) {
var res = [], n;
for(var i = 0; i < seq.length; ) {
if(breaks.indexOf(seq[i]) != -1) {
for(var j = i+1; j < seq.length; j++) {
if(breaks.indexOf(seq[j]) != -1) {
break;
}
}
n = j;
var branch = tree(seq.slice(i+1, n), breaks);
branch.unshift(seq[i]);
res.push(branch);
i+=branch.length;
} else {
res.push([seq[i]]);
i++;
}
}
return res;
}
use
tree(['A', 'WORD', 'B' , 'C' , 'WORD' , 'D'], ['WORD'])

Find duplicate object values in an array and merge them - JAVASCRIPT

I have an array of objects which contain certain duplicate properties: Following is the array sample:
var jsonData = [{x:12, machine1: 7}, {x:15, machine2:7},{x:12, machine2: 8}];
So what i need is to merge the objects with same values of x like the following array:
var jsonData = [{x:12, machine1:7, machine2:8}, {x:15, machine2:7}]
I like the lodash library.
https://lodash.com/docs#groupBy
_.groupBy(jsonData, 'x') produces:
12: [ {x=12, machine1=7}, {x=12, machine2=8} ],
15: [ {x=15, machine2=7} ]
your desired result is achieved like this:
var jsonData = [{x:12, machine1: 7}, {x:15, machine2:7},{x:12, machine2: 8}];
var groupedByX = _.groupBy(jsonData, 'x');
var result = [];
_.forEach(groupedByX, function(value, key){
var obj = {};
for(var i=0; i<value.length; i++) {
_.defaults(obj, value[i]);
}
result.push(obj);
});
I'm not sure if you're looking for pure JavaScript, but if you are, here's one solution. It's a bit heavy on nesting, but it gets the job done.
// Loop through all objects in the array
for (var i = 0; i < jsonData.length; i++) {
// Loop through all of the objects beyond i
// Don't increment automatically; we will do this later
for (var j = i+1; j < jsonData.length; ) {
// Check if our x values are a match
if (jsonData[i].x == jsonData[j].x) {
// Loop through all of the keys in our matching object
for (var key in jsonData[j]) {
// Ensure the key actually belongs to the object
// This is to avoid any prototype inheritance problems
if (jsonData[j].hasOwnProperty(key)) {
// Copy over the values to the first object
// Note this will overwrite any values if the key already exists!
jsonData[i][key] = jsonData[j][key];
}
}
// After copying the matching object, delete it from the array
// By deleting this object, the "next" object in the array moves back one
// Therefore it will be what j is prior to being incremented
// This is why we don't automatically increment
jsonData.splice(j, 1);
} else {
// If there's no match, increment to the next object to check
j++;
}
}
}
Note there is no defensive code in this sample; you probably want to add a few checks to make sure the data you have is formatted correctly before passing it along.
Also keep in mind that you might have to decide how to handle instances where two keys overlap but do not match (e.g. two objects both having machine1, but one with the value of 5 and the other with the value of 9). As is, whatever object comes later in the array will take precedence.
const mergeUnique = (list, $M = new Map(), id) => {
list.map(e => $M.has(e[id]) ? $M.set(e[id], { ...e, ...$M.get(e[id]) }) : $M.set(e[id], e));
return Array.from($M.values());
};
id would be x in your case
i created a jsperf with email as identifier: https://jsperf.com/mergeobjectswithmap/
it's a lot faster :)

What is the concept of Array.map?

I am having problems understanding the concept of Array.map. I did go to Mozilla and Tutorials Point, but they provided very limited info regarding this.
This is how I am using Array.map. It is a little complex (a bit of d3.js involved; just ignore it)
var mapCell = function (row) {
return columns.map(function(column) {
return { column : column, value : getColumnCell(row, column) }
})
}
//getColumnCell is a function defined in my code
//columns is array defined at the top of my code
I do not understand exactly what this code is doing. I know its returning a new array and stuff but this part is a little tricky!
If you want to go through my code: http://jsfiddle.net/ddfsb/2/
I am using console to actually understand what's happening inside the code. Looking at the answers provided, I have clearly understood the concept of array.map. Now the only part remaining is parameters rows and columns, but there is a difference between row and rows, and column and columns in the fiddle provided
var rows//completely ok
var columns//completely ok
funcion(row)//here,source of row is unknown.getColumncell function utilizes this parameter further making it more critical
function(column)//source of column is unknown..getColumncell function utilizes this parameter further making it more critical
Let's rewrite it a bit, and start working from inside out.
var mapCell = function (row) {
return columns.map(
function(column) {
return {
column : column,
value : getColumnCell(row, column)
}
}
)
}
The function(column) part is essentially a function that takes a column as a parameter, and returns a new object with two properties:
column, that is the original value of the parameter, and
value, that is the result of calling the getColumnCell function on the row (external variable) and column (parameter)
The columns.map() part calls the Array.map function, that takes an array and a function, and runs the function for every last item of it, and returns the results. i.e. if the input is the array [1, 2, 3, 4, 5] and the function is something like isEven, the result will be the array [false, true, false, true, false]. In your case, the input are the columns, and the output is a list of objects, each of which has a column and a value properties.
Lastly, the var mapCell = function (row) part declares that the variable mapCell will contain a function of one variable called row - and this is the same row that is used in the inner function.
In a single sentence, this line of code, declares a function that when run, will take a row and return values for all columns for that row.
map loops through your original array and calls the method for each value in the array. It collects the results of your function to create a new array with the results. You are "mapping" the array of values into a new array of mapped values. Your code is equivalent to:
var mapCell = function (row) {
var result = [];
for (var i = 0; i < columns.length; ++i) {
var mappedValue = {
column: columns[i],
value : getColumnCell(row, columns[i])
};
result.push(mappedValue);
}
return result;
};
Understanding the map function is only part of the solution here, there is also the function mapCell. It takes one parameter row and it returns something like:
[ {
"column": "parties",
"value": [cell value]
}, {
"column": "star-speak",
"value": [cell value]
} ]
Where the cell value depends on the row and the column (parties, stars-speak etc.)
A map function applies a transformation to a value, and returns that transformed value.
A simple example:
function square(x) { return x * x; }
[ 2, 3, 4 ].map(square); // gives: [ 4, 9, 16 ]
Similarly:
[ "parties", "starspeak" ].map(function (column) {
return {
column: column,
value: findTheValue(column)
}
});
Now since that map is nested with a function that gets a row parameter. You can use it in the map function, to get:
function (row) {
return [ "parties", "starspeak" ].map(function (column) {
return {
column: column,
value: findTheValue(row, column)
}
});
}
And this gets pretty close to your code.
Map function goes through each element of an array in ascending order and invokes function f on all of them.
It returns new array which is being computed after function is invoked on it.
Syntax:
array.map(f)
Example:
<!doctype html>
<html>
<head>
<script>
var arr = [4,5,6];
document.write(arr.map(function(x){return x*2;}));
</script>
</head>
</html>
Answer: 8,10,12
Summary
Array.map is a function which is located on Array.prototype.map. The function does the following:
Creates a new array with the same amount of entries/elements.
Executes a callback function, this function receives and current array element as an argument and returns the entry for the new array.
Returns the newly created array.
Example:
Basic usage:
const array = [1, 2, 3, 4];
// receive each element of array then multiply it times two
// map returns a new array
const map = array.map(x => x * 2);
console.log(map);
The callback function also exposes an index and the original array:
const array = [1, 2, 3, 4];
// the callback function can also receive the index and the
// original array on which map was called upon
const map = array.map((x, index, array) => {
console.log(index);
console.log(array);
return x + index;
});
console.log(map);
Probably most people coming here (like me) just want a basic array.map usage example:
myArray = [1,2,3]
mappedArray = [];
mappedArray = myArray.map(function(currentValue){
return currentValue *= 2;
})
//myArray is still [1,2,3]
//mappedArray is now [2,4,6]
This is it at it's most basic. For additional parameers, check out: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
IF you have an array of elements and you have to perform the same operation on the
each element of the array that time you can use the javascript map function for array
it helps to iterate throw the array then we can perform the operation of each element and
return it.
let NumberArray = [1,2,3,4,5,6,7,8];
let UpdatedArray = NumberArray.map( (Num , index )=>{
return Num*10;
})
console.log(UpdatedArray);
//UpdatedArray ==> [10, 20, 30, 40, 50, 60, 70, 80]
Javascript map() Syntax
arrayObj.map(callback[,context]);
arrayObj is a original array on which map() is invoked.
The map() takes 2 named arguments, First is a callback function and the second is a context object. callback function gets triggered on every element of an array.
In addition, callback function takes 3 arguments:
function callback(currentElement,index,array){
}
currentElement – This is a current elements of array which is passed to callback function
index – Index of the current element
array – complete array on which map() is applied
Out of these 3 elements, currentElement parameter is mandatory while the rest 2 parameters are optional.
However, map() does not change the original array, it creates a new array element which is generated by callback function.
You can read more on JavaScript map function
Array map() method returns a new array.
It does not change the original array.
let array = arr.map((c, i, arr) => { //return element to new array });
here,
array is the new array that is returned.
arr is the original array on which the map method is called.
c is the current value that is being processed.
i is the index of current value.
For example:-
const originalArr = [4, 3, 2]; let newArr = originalArr.map((val) => val + val);
result:-
newArr: [8, 6, 4]
originalArr: [4, 3, 2]
in simple words you can perform operations on array using map
Examples
1.Array
let arr = ["sam","tom"]
console.log("Before map :",arr)
arr.map((d,i)=>{
arr[i] = d+"yy"
})
console.log("After map :",arr)
Examples
2.Array Of Objects
// console.log(Date.now());
let arr = [
{name:"sam",roll_no:10},
{name:"tom",roll_no:12}
]
console.log("Before map :",arr)
arr.map(d=>{
if(d.name == "sam")
{
d.name = "sammy",
d.roll_no=100
}
})
console.log("After map :",arr)

Categories