Nested for loops and multidimensional arrays - javascript

I'm trying to get my head around how nested for loops work with multidimensional arrays in JavaScipt and I'm a bit stuck on one point.
Using a stock example
var arr = [[1,2], [3,4], [5,6]];
for (var i=0; i < arr.length; i++) {
for (var j=0; j < arr[i].length; j++) {
console.log(arr[i][j]);
}
}
This outputs 1 2 3 4 5 6 which is what I expected.
However if I add numbers to the end of the outer array:
var arr = [[1,2], [3,4], [5,6], 7, 8];
for (var i=0; i < arr.length; i++) {
for (var j=0; j < arr[i].length; j++) {
console.log(arr[i][j]);
}
}
I still get the same output of 1 2 3 4 5 6 ? ?
I am confused why 7 & 8 are not being picked up by the loop. Interestingly, if I use strings instead:
var arr = [["a","b"], ["c","d"], "y", "z"];
for (var i=0; i < arr.length; i++) {
for (var j=0; j < arr[i].length; j++) {
console.log(arr[i][j]);
}
}
The output is a b c d y z, which is what I expected. Why does it behave differently for strings?

As others have mentioned, your inner loop is all about iterating the arrays found in the top level (non-nested array). It makes the assumption that all the elements in the top-level array will be nested arrays, which isn't the case. (You need to make sure that you have an array before attempting to iterate through it.) Since 7 and 8 in the top level aren't arrays, arr[i].length returns undefined for numbers, but strings are "array like" objects and do have a length property. A string of "y" has a length of 1 and so the inner loop works because it starts from zero and obtains the character at position zero in the string "array" of "y", which is "y".
But, this is a good reason not to use traditional for loops with arrays when we now have Array.forEach(), which eliminates the need for indexes to be manually managed and allows us to access the value being enumerated directly without worrying about indexes.
var arr = [[1,2], [3,4], [5,6], 7, 8];
var output = "";
// Enumerate the top-level array:
arr.forEach(function(value){
// Check to see if the item is an array
if(value instanceof Array){
// If so, enuerate that nested array
value.forEach(function(nestedValue){
// Add the nested array's value to the output
output += nestedValue + " " ;
});
} else {
// Item is not an array, just add its value to the output
output += value + " ";
}
});
console.log(output);
Oh, and by the way, I realize that this is not what you were asking about, but just as an FYI, here's a way to obtain all the values without any loops:
console.log([[1,2], [3,4], [5,6], 7, 8].toString().split(",").join(" "));

String, Array, TypedArray, Map and Set are all built-in iterables, because each of their prototype objects implements an ##iterator method. While Number is not iterable:
const iterate = v => {
for (var i = 0; i < v.length; i++) console.log(v[i])
}
iterate([1, 'two', 777]) // iterates by element
iterate('abc') // iterates by symbol
iterate(123) // does not iterate

Here's how things look like in modern Javascript.
In respect to loops, all values can be divided into "iterable" and "non-iterable". Iterable are values that you can well... iterate - with the for..of loop.
for (let item of someIterableThing)
// use item
(You do not use bare for loops - for(var i...i < length) - for the iteration, because not every iterable has length and indexes.)
Conversely, if you do for...of with a non-iterable thing, you'll get an error.
Arrays and strings are examples of iterable values, numbers are non-iterable. So when you have
[ [1,2], [3,4], "foobar" ]
all items in this array are iterable and your nested loop will work. However, in
[ [1,2], [3,4], 999]
the last item is non-iterable, and the nested loop will fail.
There's no built-in way to tell if an unknown value is iterable, you have to write a function for this:
let isIterable = x => x && x[Symbol.iterator]
(see the dedicated topic).
Then, you can use the nested loop in a safe manner:
for (let item of array)
if (isIterable(item))
for (let subItem of item)
console.log(subItem)
else
console.log(item)
As a side note, there are lots of obsolete information about Javascript on the net. The language is evolving, and things that were fine 5 years ago, are considered bad practice nowadays. Unfortunately, most tutorials, books and teachers do not keep up, and still promote old practices, like using bare for loops.
(Since people asked why exactly bare for loops are bad, consider this example:
You have an array text containing strings and multiple functions that process this array. Programmer A writes these functions in the old-fashioned manner:
for (var i = 0; i < text.length; i++)
do_something_with(text[i]) // ;(
Programmer B writes them in the modern way:
for (let str of text)
do_something_with(str) // :)
Now, the text grows bigger and bigger and doesn't fit in memory anymore. So the system architect decided to replace it with a streamable file object that only yields one string at a time. Programmer A now has to rewrite all his 100 functions to adapt to the new interface:
for (var file = TextFile; !file.eof(); file.getNext())
do_something_with(file.currentLine)
which involves lots of pain, convulsions and headache.
Programmer B just enjoys her vacation.)

You have a double loop. When you are doing the statement console.log(arr[i][j]); you try first to go into the index i of the arr array in this part arr[i].
You then proceed to call the index of the value in the array with [j]. Because 7, and 8 are just numbers and not arrays they don't log.
This is the reason why the letters are logged:
var arr = [["a","b"], ["c","d"], "y", "z"];
for (var i=0; i < arr.length; i++) {
for (var j=0; j < arr[i].length; j++) {
/* console.log(arr[i][j]); */
}
}
var foo = "a";
var bar = foo[0];
console.log(bar);
Strings behave as arrays of characters that's why you get the letter returns itself when you call foo[0] it returns a.

Why does it behave differently for strings?
Because strings are treated as an array of characters.
A JavaScript string stores a series of characters like "John Doe". Each character can be accessed using an array index, and also has length method for getting the size of the string. Hence why your "y", "z" works but not 7, 8 as they are not an array.

Related

Trying to understand why my function won't remove more than one instance of the value arguments[i] from newArr [duplicate]

This question already has answers here:
How to filter an array from all elements of another array
(24 answers)
Closed 8 months ago.
function destroyer(arr) {
const newArr = [...arguments[0]]
for(let i = 0; i < newArr.length; i++){
for(let j = 1; j < arguments.length; j++){
if(arguments[j] == newArr[i]){
newArr.splice(i,1)
console.log(newArr)
}
}
}
}
destroyer([3, 5, 1, 2, 2], 3, 5, 2);
New JS learner here.
Working on a problem that is supposed to look through the first arg in destroyer which will be an array and remove the elements that match the arguments following the array.
Results are [1,2] in the console output. Intended results are [1] with the given parameters Upon further testing it seems like the destroyer function is only removing the first instance of any value that it matches in newArr. If I take the second instance of '2' out of the test set it behaves as intended. I'm trying to understand what in my logic here is wrong. I've tried several different iteration patterns and can't seem to see what the problem is.
Thanks for any help!
I am going to link a few other answers here is this seems to be a fairly common question. The gist is that you are iterating over the live array while removing items from that array resulting in potentially skipping items.
How to iterate over an array and remove elements in JavaScript
Remove multiple elements from array in Javascript/jQuery
Alternatively you could also the filter method to help remove multiple values:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
Try this:
function destroyer(arr) {
const newArr = [...arguments[0]]
for(let i = newArr.length - 1; i >= 0; i--){
for(let j = 1; j < arguments.length; j++){
if(arguments[j] == newArr[i]){
newArr.splice(i,1)
console.log(newArr)
}
}
}
}
This is an array pointer issue.
when i = 3 and j = 3, the function will match arguments[3] == newArr[3].
At this moment, newArr will be removed 1 element which is the first 2, and then the newArr becomes an new array that is [3,5,1,2].
The next index i is 4 which doesn't exist in the new newArr. So, the function will return and finish. That's why you get [3,5,1,2].
function destroyer (arg) {
if(!Array.isArray(arg)) return arg;
return arg.filter(x => !Array.prototype.slice.call(arguments, 1).includes(x))
}
It is because newArr is getting shorter as you splice it through your loops and the loop itself is also shortened.
see this probably your solution

Populate array with non repeating random numbers

So I have an array called arr.
I want to populate it with four random numbers:
for (var i = 0; i < 4; i++) {
arr[i] = Math.floor(Math.random() * 10);
}
If that returns an array with values [4, 2, 3, 4]. How do I check for duplicates, and recalculate a random number that doesn't equal any other values that already exist in the array?
Also if there is a better way of accomplishing this, I would love to learn/know that way as well.
Also if there is a better way of accomplishing this, I would love to learn/know that way as well.
There is.
If you need unique values, generate a regular sequential array [1,2,3,4] (or however long you need), and then use that to fill a second array, by successively extracting random elements from the first array (thus growing array 2, and shrinking array 1):
var a1 = [];
for (var i=0; i<4; i++) { a1.push(i); }
var a2 = [];
while (a1.length) {
var pos = Math.random()*a1.length;
var element = a1.splice(pos, 1)[0];
a2.push(element);
}
// a2 is now an array with a random permutation of the elements of a1
The splice call removes an subarray starting at position pos with (in this case) 1 element, so a1 gets shorter and shorter, until it's empty, at which point a2 will be a permuted array of unique values.
If we start with a1 = [0,1,2,3] then after this runs a2 can be any of 24 possible sequences, with guaranteed unique values because that's what we started with, just in a randomised order.
What you are looking for is a way to take a random sample from an array of the integers [0..9]. You can do this with several algorithms, for example a simple reservoir sampling algorithm.
If you want to have an array that contains any amount of numbers from 1 to n in random order, I think the more viable approach is to generate an array with all numbers from 1 to n and then shuffle it as described here.
You can then proceed to splice the array after shuffling to shorten it to the desired length.
Both steps have the complexity of O(1) or O(n), which is far better than testing for every insert or modifying a value pool from which you draw the numbers.
I'd probably make a function for the loop.
function getNumber(arr){
var num = Math.floor(Math.random() * 10);
if(arr.indexOf(num) < 0){
return num;
} else {
getNumber(arr);
};
};
Then you would do:
var arr = [];
for (var i = 0; i < 4; i++) {
arr[i] = getNumber(arr);
}

Javascript Arrays - Find Duplicates [duplicate]

This question already has answers here:
Get all non-unique values (i.e.: duplicate/more than one occurrence) in an array
(97 answers)
Closed 8 years ago.
Here is my question...
Given an array populated with numbers as a function parameter, produce a resulting array which contains any duplicates number from the array.
For example, given the array [ 1, 2, 4, 4, 3, 3, 1, 5, 3 ] it should return [1, 4, 3]. For extra bonus points return a sorted array.
I am starting out with Javascript - I know the language however, using it in the correct way ( as one should ) I'm still getting to grips with.
My pseudo code for this would be to:
Create an array with the numbers above var numbers = [1, 2, 4, 4, 3, 3, 1, 5, 3];
Then create an empty array named "result" var result = [];
Create a for loop that goes through the var numbers to check for duplicates which will then populate the empty array "result" with the duplicates
for (var i = 0;i < numbers.length; i++) {
//This is where I'm stuck...
}
I'm not sure what to do within the for loop to populate the var result and to throw in to the mix... The given array has to be a function parameter which makes sense so you can change the numbers in one place.
Any feedback on my thought process on this so far is greatly appreciated but ultimately I am wanting to learn how to achieve this.
Here is a JSFiddle of my progress so far... http://jsfiddle.net/fbauW/
One way of doing this (and it's not the only way) is by checking for existing elements in the array. Take a look at JavaScript's lastIndexOf function:
http://www.w3schools.com/jsref/jsref_lastindexof_array.asp
It will return -1 if the object does not exist in your array, and if it exists, will return an index of a later position than you are in. So you can use an if statement in your loop that checks whether or not there is another index containing your number, and add it in to your results array IF AND ONLY IF the index you get back != the index you are currently on (if they equal, this means that there is only one of that element in the list).
If you need more help, comment here and I can type some code in!
Good luck!
Array.prototype.contains = function(k) {
for ( var p in this)
if (this[p] === k)
return true;
return false;
};
//this prototype function checks if an element is already in the array or not
//go through all the array and push the element to result if it is not
//this way we can eliminate duplicates
//result will contain the resultant array
function findDuplicates(Numbers) {
var arrayLength = Numbers.length, i, j, result = [];
for (i = 0; i < arrayLength; i++) {
for (j = 0; j < arrayLength; j++) {
if (a[i] == a[j] && i != j && !result.contains(a[i])) {
result.push(a[i]);
}
}
}
return result;
}

Undo sort on sorted array in javascript

I have an array. I sort it.
I get a second array which is already sorted based on the first one.
I need to reverse the sorting on the second array.
For example, if the first array (unsorted) is: [9, 5, 3, 0, 2] then I want to to sort it, so that it becomes [0, 2, 3, 5, 9].
Then I receive the second array sorted based on the first one, for example ["home", "car", "train", "pc", "mouse"]. I need it to become ["mouse, "pc", "train", "home", "car"].
I can't make a copy of the array.
I have the following code:
//data_r is an array with values
var i = 0;
var sort_order = new Array();
data_r.sort(function (a,b) {
var res = a[0] - b[0];
sort_order[i] = res;
i++;
return res;
});
In the end, the the sort_order array will contain the actions performed when we sorted items. If I want to sort a second array exactly the same way as the first then I can do the following:
//data_x is an array with values
var i = 0;
data_x.sort(function (a,b) {
i++;
return sort_order[i-1];
});
Now the data_x array is sorted exactly the same way as the data_r array.
How can I undo sort on the data_r array?
The following code is incorrect:
var unsort = new Array();
for(var i = 0; i < data_r.length; i++)
unsort[i] = sort_order[i]*(-1);//-1 so we perfom the oposite action
Your premise here is flawed.
In the end, the sort_order array contains the actions performed when we sorted items.
No, it doesn't; it contains a log of the comparisons performed by the Javascript Array.sort function. The actions it took in response to those comparison results are private to it.
If I want to sort a second array exactly the same way as the first then I can do the following:
This is not guaranteed to work. Even if the two arrays are the same size, Array.sort may not always compare the same elements in the same order each time it's called - it's possible that it's using a randomized algorithm, that it performs comparisons based on other data that are internal to the interpreter, or that it switches between multiple entirely different sort algorithms under some circumstances.
While this code may work for you, right now, in your current web browser, it is likely to fail in surprising ways in other circumstances (possibly in future browsers). Do not use this technique in production code.
The question is, how can i unsort the data_r array?
Make a copy of the array before you sort it.
Storing res[i] = a - b is like journaling the sort() algorithm - but what if it used a random pivot?
This code is inherently unreliable unless you write sort() yourself. It's also inefficient.
A better approach, one that will solve both your needs, is to create an array of indices and sort that. This is trivial to invert. Then you can implement a permute function that takes an array of indices, and it achieves a sort or unsort, depending on the input.
If x is from 0:n-1, create an array sort_i of same size, then initialize each sort_i[i] = i.
for(var i = 0; i < n; i++)
sort_i[i] = i;
Then
sort_i.sort(function (a,b) { return x[a] - x[b]; });
Now you have the indices. To apply to x:
for(var i = 0; i < n; i++)
sort_x[i] = x[sort_i[i]];
To unsort it, first invert the indices
for(var i = 0; i < n; i++)
unsort_i[sort_i[i]] = i;
Then apply the indices. Exercise left to question asker.
This approach of sorting an array of integer indices is needed when you don't want to move the original elements around in memory (maybe they are big objects), and many other circumstances. Basically you are sorting pointers. The result is an index to the data, and a reverse index.
See #duskwuff's answer on why your approach doesn't work.
Instead, just introduce a mapping between the original data and the sorted data.
{0:2, 1:3, 2:1, 3:0}
Which means the first element became the third, the second became the last and so on. Below we'll use an array instead of an object.
Why does this map help? You can sort it like another dataset by just using the indizes in it as pointers to the data you're going to compare. And you can apply the mapping easily on other datasets. And you can even reverse that mapping very easily. See it in the code:
// data_r, data_x are arrays with values
var l = data_r.length;
var sort_order = new Array(l);
for (var i=0; i<l; i++) sort_order[i] = i; // initialised as 1-1 mapping
// change the sort_order first:
sort_order.sort(function (a,b) {
// a and b being indices
return data_r[a] - data_r[b];
});
// Making a new, sorted array
var data_x_sorted = new Array(l);
for (var i=0; i<l; i++)
data_x_sorted[ sort_order[i] ] = data_x[i]; // put it to sorted position
If you want to sort the data_x array itself, just use the "apply" algorithm which I showed for data_r.
The question is, how can I undo sort on the data_r array?
Either don't sort it at all, and just make a copy of it which gets sorted (or do nothing at all).
Or use the sort_order to reverse it. You just would need to swap i and newIndex (sortOrder[i]) everywhere. Example for building a new, "unsorted" (old-order) array:
var unsorted = new Array(l);
for (var i=0; i<l; i++)
unsorted[i] = data_r[ sort_order[i] ]; // take it from its new position
While this question is 8 years old at this point, I came across it when trying to find the same solution to the problem and I was unable to find a suitable, performant, and intuitive way of doing so, so I wrote one myself.
Please take a look at the sort-unwind library. If ranks is a list of indexes that would rank an array in order...
import unwind from 'sort-unwind'
const suits = ['♥', '♠', '♣', '♦']
const ranks = [2, 0, 3, 1]
const [sortedSuits, tenet] = unwind(ranks, suits)
// sortedSuits <- ['♠', '♦', '♥', '♣']
// unwind <- [1, 3, 0, 2]
You can then use the tenet variable that's returned to unsort an array and restore the original ordering.
const names = ['spades', 'diamonds', 'hearts', 'clubs']
const [tenetNames, tenetRanks] = unwind(tenet, names)
// tenetNames <- ['hearts', 'spades', 'clubs', 'diamonds']
// tenetRanks <- [2, 0, 3, 1]
The sort function just returns a number which can be positive,zero, or negative telling it if the current element goes before,has same weight, or goes after the element it is comparing it too. I would imagine your sort order array is longer than your data_r array because of the number of comparisons you make. I would just make a copy of data_r before you sort it and then set data_r equal to that array when you want it unsorted.
If you have a lot of these arrays to maintain, it might be as well to
convert array1 into an array of objects, each one containing the value
and its original position in the array. This keeps everything together
in one array.
var array1 = [9, 5, 3, 0, 2];
var array2 = ["home", "car", "train", "pc", "mouse"];
var sort = function(array){
var indexed_objects = array.map(function(value, index){
return {index: index, value: value};
});
indexed_objects.sort(function(a,b){
return a.value <= b.value ? -1 : 1;
});
return indexed_objects;
};
var sorted1 = sort(array1);
sorted1; // [{index: 3, value:0}, {index: 4, value: 2}, ...]
And now, given an array of sorted objects, we can write a function to
unsort any other array accordingly:
var unsort = function(array, sorted_objects){
var unsorted = [];
sorted_objects.forEach(function(item, index){
unsorted[item.index] = array[index];
});
return unsorted;
};
var array2_unsorted = unsort(array2, sorted1);
array2_unsorted; // ["mouse", "pc", "train", "home", "car"]
v1 = [0,1,2,3,4,5,6]
q = v1.length
b = []
for(i=0;i<q;i++){
r = parseInt(Math.random()*v1.length)
b.push(v1[r])
a = v1.indexOf(v1[r])
v1.splice(a,1)
}

Find a pair of elements from an given array whose sum equals a specific target number in JavaScript

In Javascript is any other efficient way to achieve this task?
I tried as:
const a1 = [1,3,4,2,5,7,8,6];
var newArray =[];
function fun(a,n){
for(let i = 0; i<a.length; i++){
for(let j=i+1; j<a.length; j++){
if((a[i]+a[j])==n){
newArray.push([a[i],a[j]]);
}
}
}
}
fun(a1, 10)
console.log(newArray);
Here output:
[(3,7),(4,6),(2,8)]
The question is tagged javascript, but this answer is basically language agnostic.
If the array is sorted (or you can sort it), you can iterate over the array and for each element x in it binary search for (target-x) in the array. This gives you O(nlogn) run time.
If you can use extra memory, you can populate a dictionary with the elements of the array, and then for each element x in the array, look up the dictionary for (target-x). If your dictionary is implemented on a hashtable, this gives you O(n) run time.
I think your method makes sense from a brute force perspective.
In terms of optimization, a few things come to mind.
Do duplicates count? If not, you can remove all duplicates from
the starting lists.
You can sort the lists in ascending order,
and skip the remainder of the inner loop when the sum exceeds the
target value.
This is a general programming problem commonly referred to as the "two sum problem", which itself is a subset of the subset sum problem.
But nevertheless can be solved efficiently and I used this article as inspiration.
const a1 = [1, 3, 4, 2, 5, 7, 8, 6];
// our two sum function which will return
// all pairs in the array that sum up to S
function twoSum(arr, S) {
const sums = [];
const hashMap = new Map();
// check each element in array
for (let i = 0; i < arr.length; i++) {
// calculate S - current element
let sumMinusElement = S - arr[i];
// check if this number exists in hash map
// if so then we found a pair of numbers that sum to S
if (hashMap.has(sumMinusElement.toString())) {
sums.push([arr[i], sumMinusElement]);
}
// add the current number to the hash map
hashMap.set(arr[i].toString(), arr[i])
}
// return all pairs of integers that sum to S
return sums;
}
console.log(twoSum(a1, 10))
Here I use the Map object since I think it's faster when checking if number already exists, but I might be wrong and you can use just a plain object, as in the article, if you want.

Categories

Resources