forEach works - why not for loop? - javascript

I'm working on the flatten kata at codewars.com - my code closely resembles a solution I've found, so I feel like my logic is on the right track. But I can't seem to get my code to work and I don't know if it's a dumb syntax error or if I'm doing something fundamentally incorrect.
Instructions:
Write a function that flattens an Array of Array objects into a flat Array. Your function
must only do one level of flattening.
flatten([[1,2,3],["a","b","c"],[1,2,3]]) // => [1,2,3,"a","b","c",1,2,3]
Working solution using forEach:
var flatten = function (lol){
var res = [];
lol.forEach(function (x) {
if (x instanceof Array)
res = res.concat(x);
else
res.push(x);
});
return res;
}
My code using for loops:
var flatten = function (array){
var newArray = [];
for (i = 0; i < array.length; i++) {
if (i instanceof Array)
for (e = 0; e < i.length; e++) {
newArray.push(e);
}
else
newArray.push(i);
}
return newArray;
}

The most important reason why it isn't working is that you are treating your indices (i and e) as if they were the actual array elements (hence, the sub Arrays themselves). i is not the actual array, and does not have any array properties. It is just a number.
Each element must be referenced via the array[index], so in the case of the array argument, in the top loop, you would check array[i], but most importantly, if it is not an array, that is what you would push().
In your inner loop, you face a similar issue with e. However, you cannot simply do array[e] as the array you would be looking at would be array[i]. The proper way to address this is to make another variable for the array OR simply array[i][e]. Again, this is the value you would push().
I understand that this answer is a little vague, but it is intentionally so, as this is obviously an assignment from which you are trying to learn.

You need to use the value of the original array to push/concat into the new one. Also, you don't need to check the type, you can just concat everything:
var flatten = function (array) {
var newArray = [];
var arrayLength = array.length;
for (i = 0; i < arrayLength; i++) {
newArray = newArray.concat(array[i]);
}
return newArray;
}

if (array[i] instanceof Array)
Your algorithm looks fine, but you are referencing a Number index where you mean to be referencing an array element. Fix this in 3 places and your code should work.

Spoiler:
var flatten = function (array){
var newArray = [];
for (i = 0; i < array.length; i++) {
if (array[i] instanceof Array)
newArray = newArray.concat(array[i]);
else
newArray.push(array[i]);
}
return newArray;
}

Related

Javascript: add value to array while looping that will then also be included in the loop

Sorry if this is a dupplicate, can't seem to find it.
var a = [1,2,3,4];
a.forEach(function(value){
if(value == 1) a.push(5);
console.log(value);
});
I wonder if there is a way (any sort of loop or datatype) so that this will ouput 1 2 3 4 5 during the loop (or in any order, as long as all the 5 numbers are in there)
Using Array.prototype.forEach() will not apply the callback to elements that are appended to, or removed from, the array during execution. From the specification:
The range of elements processed by forEach is set before the first
call to callbackfn. Elements which are appended to the array after the
call to forEach begins will not be visited by callbackfn.
You can, however, use a standard for loop with the conditional checking the current length of the array during each iteration:
for (var i = 0; i < a.length; i++) {
if (a[i] == 1) a.push(5);
console.log(a[i]);
}
Obvious solution :
var a = [1,2,3,4];
for (var i=0; i<a.length; i++){
var value = a[i];
if(value == 1) a.push(5);
console.log(value);
}
Since you appear to use Harmony, how about a generator like this:
function *iter(array) {
for (var n = 0; n < array.length; n++)
yield array[n];
}
and then
var a = [1,2,3,4];
for(var p of iter(a)) {
if(p == 1) a.push(5)
console.log(p)
}
prints 1 2 3 4 5
If you need a method that uses a callback as apposed to the for loop reduce will work.
var a = [1,2,3,4];
var result = a.reduce(function(total, item) {
total.push(item);
if (item === 4) total.push(5);
return total;
}, []);
console.log(result);
Better Alternate solution:
if (a.indexOf(1) >= 0) a.push(5);
a.forEach( ... );
OK, maybe not strictly better, since it traverses the array twice. However you ought to consider why you're trying to modify the array whilst iterating over it in the first place. It's an unusual situation.
SO this is a quick test that seems to work well :
var a = [1,2,3,4,5];
a.forEach (function (val,index,array) {
if (val == 5) {
array.push (6);
};
console.log (val);
});
a.forEach(function fn(item, index) {
if (item == 5) {
array.push (6);
};
})
var a = [1,2,3,4];
Object.keys(a).forEach(idx => {
if(a[idx]==1) a.push(5);
console.log(a[idx]);
});
OP here. I've since learned that ES6 Sets do include items added during forEach.
So if you can use a set instead of an array, you can do:
var a = new Set([1,2,3,4]);
a.forEach(function(value){
if(value == 1) a.add(5);
console.log(value);
});
which does indeed log 5 as well.
Just an alternative to the for loop. Of course Sets are different than arrays in several ways, so you'd need to understand the differences and how it effects your other code.

Search 2d javascript array for a known complete array

I have a 2d array similar to this:
var array = [
{100,200},
{200,200},
{100,400}
];
Now I want to find if a known array exists inside the 2d array. For example, I want to check if [200,200] exists as a 2nd level array inside the 2d array.
On 1d arrays in the past I've used something like:
if (value in array) {...}
Can't seem to get that method working on 2d. What is the best solution?
Not sure if you already know but your syntax is not correct. It should look like this:
var array = [
[100,200],
[200,200],
[100,400]
];
A naive way to check if [200, 200] exists as a 2nd level array:
console.log(array[1][0] == 200 && array[1][1] == 200);
Another naive approach is to use a nested loop and go through each item.
If you want a fast approach to this, you might want to read up on search algorithms.
Searching Algorithms
var array = [
[100,200],
[200,200],
[100,400]
];
var searchFor = [200,200];
function arrayExistsInside(haystack, needle) {
for(var i = 0; i < haystack.length; i++) {
if(compareArray(haystack[i], needle)) return true;
}
return false;
}
function compareArray(array1, array2) {
if(array1.length != array2.length) return false;
for(var i = 0; i < array1.length; i++) {
if(array1[i] != array2[i]) return false;
}
return true;
}
if(arrayExistsInside(array, searchFor)) { ... }
You could also use the compare function outlined on How to compare arrays in JavaScript?

Only return values found in all arrays

Simple question, but i dont know how to solve it
I have several arrays, but i only want the values that all arrays have in common
Im using javascript.
Try looking for the value in each of the arrays using indexOF.
I never knew IE didn't support indexOf, but here's a quick fix from this post.
Something like this should work:
function getCommonElements() {
var common = [],
i, j;
if (arguments.length === 0)
return common;
outerLoop:
for (i = 0; i < arguments[0].length; i++) {
for (j = 1; j < arguments.length; j++)
if (-1 === arguments[j].indexOf(arguments[0][i]))
continue outerLoop;
common.push(arguments[0][i]);
}
return common;
}
Call it with any number of arrays as arguments:
var commonEls = getCommonElements(arr1, arr2, arr3, etc);
In case it's not obvious, the idea is to loop through the array from the first argument and test each of its elements against the other arrays. As soon as a particular element is found to not be in any of the other arrays from the other arguments continue on with the next element. Otherwise add the current element to the output array, common.
If you need to support browsers (IE < 9) that don't support the Array.indexOf() method you can either include the shim shown at the MDN page or replace the .indexOf() test from my code with another loop.
I think this should work.
var arr1 = [1,2,3,4]
, arr2 = [2,3,4,5]
, arr3 = [3,4,5,6]
, arrs = [arr1, arr2, arr3];
var all = arr1.concat(arr2.concat(arr3)).sort()
, red1 = all.filter(
function(val, i, arr) {
return i === arr.lastIndexOf(val)-1;
})
, red2 = red1.filter(
function(val, i, arr) {
var shared = true;
arrs.forEach(
function(arr, i, src) {
if (arr.indexOf(val) === -1)
shared = false;
})
return shared;
})
If you are only concerned with modern browsers that support reduce(), then use this solution:
Finding matches between multiple JavaScript Arrays
If you must support IE6, then use my solution below. Here's how I got this to work in IE6 using jQuery:
// Find common values across all arrays in 'a',
// where 'a' is an array of arrays [[arr1], [arr2], ...]
Object.common = function(a) {
var aCommon = [];
for (var i=0,imax=a[0].length,nMatch,sVal; i<imax; i++) {
nMatch = 0;
sVal = a[0][i];
for (var j=1,jmax=a.length; j<jmax; j++) {
nMatch += ($.inArray(sVal, a[j])>-1) ? 1 : 0;
}
if (nMatch===a.length-1) aCommon.push(sVal);
}
return aCommon;
}
Basically, you just loop through each value of the first array in 'a' to see if it exists in the other arrays. If it exists, you increment nMatch, and after scanning the other arrays you add the value to the aCommon array if nMatch equals the total number of the other arrays.
Using the sample data provided by Florian Salihovic, Object.common(arrs) would return [3, 4].
If you cannot use jQuery, then replace $.inArray() with the code provided by Mozilla:
https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/IndexOf

Removing duplicates from an array in Javascript [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.
var data = localStorage.getItem('oldData').split(" ");
I am accessing localStorage as above and getting an array of values. Some of the elements are repeated in the string value for oldData, for example:
apples oranges apples apples
I want data to have only two elements apples and oranges. How can I do this in Javascript?
Array.prototype.unique = function(){
return Object.keys(this.reduce(function(r,v){
return r[v]=1,r;
},{}));
}
Strap it on. It's O(n) because using an object just requires you to loop through the array once and assign every value in it as a key, overwriting as you go. This only works when the values are primitives (or you have Harmony WeakMaps). But that's almost always the kind of array you want to do this one so it works out.
For bonus points here's the second best way to do it. This is at minimum twice as fast as the normal double loop answers and is at minimum as good as the ones requiring presorting,
(but still worse than the above hash method which is infinitely faster).
Array.prototype.unique = function(){
return this.filter(function(s, i, a){
return i == a.lastIndexOf(s);
});
}
The reason it beats every other answer aside from the hash is because it's able to get the benefit of sorting without doing the sorting step. It only searches from the current item forward, and from the other end in reverse, so there will never be a case where two items are checked against each other twice, and there will never be an unnecessary comparison done because it always quits at the very minimum amount of work needed to make a final decision. And it does all of this with the minimum possible creation of placeholder variables as a bonus.
first is to insert one value in your array by using push
var array = [];
array.push("newvalue");
then the next insertion of value, check if your value is existing in your array using "for loop". then if the value does not exist, insert that value using push() again
Array.prototype.unique = function()
{
var a = [];
var l = this.length;
for(var i=0; i<l; i++)
{
for(var j=i+1; j<l; j++)
{ if (this[i] === this[j]) j = ++i; }
a.push(this[i]);
}
return a;
};
Something like this should do the trick:
uniqueValues = function(array) {
var i, value,
l = array.length
set = {},
copy = [];
for (i=0; i<l; ++i) {
set[array[i]] = true;
}
for (value in set) {
if (set.hasOwnProperty(value)) {
copy.push(value);
}
}
return copy;
}
This is what I have used finally
var data = localStorage.getItem('oldData').split(" ");
var sdata = data.sort();
var udata = [];
var j = 0;
udata.push(sdata[0]);
for (var i = 1; i < data.length - 1; i += 1) {
if (sdata[i] != udata[j]) {
udata.push(sdata[i]);
j++;
}
}

Get first element of a sparse JavaScript array

I have an array of objects in javascript. I use jquery.
How do i get the first element in the array? I cant use the array index - as I assign each elements index when I am adding the objects to the array. So the indexes arent 0, 1, 2 etc.
Just need to get the first element of the array?
If you don't use sequentially numbered elements, you'll have to loop through until you hit the first one:
var firstIndex = 0;
while (firstIndex < myarray.length && myarray[firstIndex] === undefined) {
firstIndex++;
}
if (firstIndex < myarray.length) {
var firstElement = myarray[firstIndex];
} else {
// no elements.
}
or some equivalently silly construction. This gets you the first item's index, which you might or might not care about it.
If this is something you need to do often, you should keep a lookaside reference to the current first valid index, so this becomes an O(1) operation instead of O(n) every time. If you're frequently needing to iterate through a truly sparse array, consider another data structure, like keeping an object alongside it that back-maps ordinal results to indexes, or something that fits your data.
The filter method works with sparse arrays.
var first = array.filter(x => true)[0];
Have you considered:
function getFirstIndex(array){
var result;
if(array instanceof Array){
for(var i in array){
result = i;
break;
}
} else {
return null;
}
return result;
}
?
And as a way to get the last element in the array:
function getLastIndex(array){
var result;
if(array instanceof Array){
result = array.push("");
array.pop;
}
} else {
return null;
}
return result;
}
Neither of these uses jquery.
Object.keys(array)[0] returns the index (in String form) of the first element in the sparse array.
var array = [];
array[2] = true;
array[5] = undefined;
var keys = Object.keys(array); // => ["2", "5"]
var first = Number(keys[0]); // => 2
var last = Number(keys[keys.length - 1]); // => 5
I was also facing a similar problem and was surprised that no one has considered the following:
var testArray = [];
testArray [1245]= 31;
testArray[2045] = 45;
for(index in testArray){
console.log(index+','+testArray[index])
}
The above will produce
1245,31
2045,45
If needed you could exist after the first iteration if all that was required but generally we need to know where in the array to begin.
This is a proposal with ES5 method with Array#some.
The code gets the first nonsparse element and the index. The iteration stops immediately with returning true in the callback:
var a = [, , 22, 33],
value,
index;
a.some(function (v, i) {
value = v;
index = i;
return true;
});
console.log(index, value);
If you find yourself needing to do manipulation of arrays a lot, you might be interested in the Underscore library. It provides utility methods for manipulating arrays, for example compact:
var yourArray = [];
yourArray[10] = "foo";
var firstValue = _.compact(yourArray)[0];
However, it does sound like you are doing something strange when you are constructing your array. Perhaps Array.push would help you out?

Categories