Identical for loop doesn't seem to be working - javascript

I'm trying to solve a coding challenge from Free Code Camp.
Diff Two Arrays
Compare two arrays and return a new array with any items only found in one of the two given arrays, but not both. In other words, return the symmetric difference of the two arrays.
[1, 2, 3, 5], [1, 2, 3, 4, 5] should return [4].
[1, "calf", 3, "piglet"], [1, "calf", 3, 4] should return ["piglet", 4].
My code is below:
function diffArray(arr1, arr2) {
var arr1Key = [];
arr1Key = arr1;
for (i = arr1.length - 1; i >= 0; i--) {
for (j = 0; j < arr2.length; j++) {
if (arr1[i] === arr2 [j]) {
arr1.splice(i, 1);
}
}
}
for (k = arr2.length - 1; k >= 0; k--) {
for (l = 0; l < arr1Key.length; l++) {
if (arr2[k] === arr1Key[l]) {
arr2.splice(k, 1);
}
}
}
var newArr = [];
newArr = arr1.concat(arr2);
return (newArr)
}
diffArray([1, 2, 3, 5], [1, 2, 3, 4, 5]);
I know it's probably not perfect/optimal, but what I primarily want to know is why my first series of for loops (using i & j) works perfectly. Meanwhile, my next series of for loops (using k & l) is a nearly identical copy of the first, but after some testing I found that only the k for loop works. The l for loop isn't working, and I don't understand why.
I've been staring at this thing and tinkering with it for what feels like forever and I just don't understand.

You are splicing the arrays while looping through them, splicing can make them grow or shrink and you don't want that because you are using the array's length to loop.

The problem is that you think you clone arr1 by writing arr1Key = arr1;
This does not clone but creates a reference to arr1, so arr1.splice(i, 1); changes also arr1Key.
Just change the line arr1Key = arr1; into arr1Key = arr1.slice();

Related

How to get the index of a multidimensional array and splice that array

There is a problem in FreeCodeCamp.I'm not here to search solution to that problem. While trying to solve the problem I found some lines of my code do not work. I can not understand why that is not working. So I am here to ask YOU, good people, to help me.
problem
There is a function. I will pass an array and a number to that function. And what I need to return is also an array.
The array is a multidimensional array.
what I want to do
First of all I want to check if the inner or subarray contains the number i passed while calling the function. If that contains i need the index of that number in that subarray. Then I want to delete the number from that subarray using splice(). At last I wanted to return an array where there are sub arrays in it but none of them contain the given number.
where i am stuck in
But I am stuck in finding the index of the number in sub arrays, how can i use splice() to delete the number? Is it possible to do this way? Do you have any better suggestion for me?
my code
where for the first for loop it just prints -1
for the second loop, it prints the index of the array,not the index of the subarray.
function filteredArray(arr, elem) {
let newArr = [];
// Only change code below this line
newArr = [...arr]
let L = arr.length;
for (let i = 0; i < L; i++) {
// -------1-----------
for (elem in newArr[i]) {
console.log(newArr[i].indexOf(elem));
}
}
console.log('first loop ends')
for (let i = 0; i < L; i++) {
// --------2---------
for (let j = 0; j < newArr[i].length; j++) {
if (newArr[i][j] == elem) {
console.log(newArr[i].indexOf(elem))
}
}
// Only change code above this line
return newArr;
}
}
console.log(filteredArray([[3, 2, 3], [1, 6, 3], [3, 13, 26], [19, 3, 9]], 3));
function filteredArray(arr, elem) {
let newArr = [];
// Only change code below this line
newArr = [...arr]
let L = arr.length;
// for (let i = 0; i < L; i++) {
// // -------1-----------
// for (elem in newArr[i]) {
// console.log(newArr[i].indexOf(elem));
// }
// }
console.log('first loop ends')
for (let i = 0; i < L; i++) {
// --------2---------
for (let j = 0; j < newArr[i].length; j++) {
if (newArr[i][j] == elem) {
console.log(newArr[i].indexOf(elem))
}
}
// Only change code above this line
return newArr;
}
}
console.log(filteredArray([[3, 2, 3], [1, 6, 3], [3, 13, 26], [19, 3, 9]], 3));
When removing values from an array you don't want to use a mutating method like .splice() -- the original array will change. If .splice() removes a number, the length of the array decreases and all indexes at and after the index of the removed number will shift (unless you replace that number instead). The non-mutating methods such as filter() and .map() makes a copy of the array and returns the copy leaving the original array intact. See this article for a easy reference of what mutates and what doesn't.
You could simplify the process of removing a given number from an array of arrays by using .map() on each sub-array and .filter() each sub-array with the condition of returning only numbers that do not equal the given number.
const data = [[3, 2, 3], [1, 6, 3], [3, 13, 26], [19, 3, 9]];
const filterCols = (target, arrArr) =>
arrArr.map(sub => sub.filter(num => num !== target));
console.log(filterCols(3, data));

Multi-dimensional For Loop

I have three arrays and I need to create a set of rules based on these three arrays, but I'm struggling with the logic of how to write a function that will give me every possible combination of every entry in each array. So, I have, for example:
var array 1 = [1, 2];
var array 2 = [3, 4, 5];
var array 4 = [6, 7, 8, 9, 10];
And I'd wan't get back a string, object etc of all possible combinations (which I wont attempt to work out here). So for example:
var result = ["1-3-6", "2-3-6", "1,4,6"];
And so on, so far I've tried sitting down and composing a For Loop but I'm just really not sure where to start. I also looked at maps, but could not find any examples that went this deep, so I wasn't sure if a map would get the job done either.
The actual data I want to load in, the first array has 2 entries, the second have 7 and the last one had 6, so for the workings out I've done there should be 84 entries. That was based on (Array 3 * Array 2) * Array 1.
Hope that all makes sense I know it's a bit confusing. Also worth mentioning that I'm using Angular JS so an angular solution or vanilla JS solution is preferred but not essential.
What you are looking is the Cartesian product of arrays. You can use a function like this (extracted from here):
function cartesian() {
var r = [], arg = arguments, max = arg.length-1;
function helper(arr, i) {
for (var j=0, l=arg[i].length; j<l; j++) {
var a = arr.slice(0); // clone arr
a.push(arg[i][j]);
if (i==max)
r.push(a);
else
helper(a, i+1);
}
}
helper([], 0);
return r;
}
There are lot of examples, like:
JavaScript - Generating combinations from n arrays with m elements
With recursive:
Finding All Combinations of JavaScript array values
Cartesian product of multiple arrays in JavaScript
And with multiple (N) arrays:
Combine 2 arrays into 1 in all possible ways in JavaScript
Hope it helps!
Nested for loops will do
function arrComb(arr1, arr2, arr3) {
var l1 = arr1.length,
l2 = arr2.length,
l3 = arr3.length,
i, j, k, res = [];
for (i = 0; i < l1; i++) {
for (j = 0; j < l2; j++) {
for (k = 0; k < l3; k++) {
res.push(arr1[i] + '-' + arr2[j] + '-' + arr3[k]);
}
}
}
console.log(res)
}
arrComb([1, 2], [3, 4, 5], [6, 7, 8, 9, 10]);
A bit more elegant:
var array_1 = [1, 2];
var array_2 = [3, 4, 5];
var array_4 = [6, 7, 8, 9, 10];
var result = [];
for (var a1 of array_1)
{
for (var a2 of array_2)
{
for (var a3 of array_4)
{
result.push("\""+a1+"-"+a2+"-"+a3+"\"")
}
}
}
alert("["+result+"]")

Javascript && operators not behaving in the preferred way?

I'm slowly learning JavaScript, and to this point I could figure everything out on my own. But I'm just spending waaay to much time at this problem.
Basically, I have two arrays, and if a number in the second array matches a number in the first array, it has to delete that number.
So the simplest solution I can think of is a for loop that loops through each property of the array, and if it doesn't match a number out of the second array, push it into a new array.
I have written this bit of code:
var arr = [1, 2, 3, 5, 1, 2, 3], newArr = [2, 3, 3];
var finalArr = [];
for (var i = 0; i < newArr.length; i++) {
if (arr[i] != newArr[0] && arr[i] != newArr[1] && arr[i] != newArr[2])
finalArr.push(arr[i]);
}
// ----> finalArr = [1]
The purpose is, it takes every value out of the array named "arr", compares it to a value of "newArr", and if the value doesn't match, push it into a new array.
Can anyone see the problem?
Thanks in advance!
var arr = [1, 2, 3, 5, 1, 2, 3], newArr = [2, 3, 3];
var finalArr = [];
for (var i = 0; i < arr.length; i++) {
if (arr[i] != newArr[0] && arr[i] != newArr[1] && arr[i] != newArr[2])
finalArr.push(arr[i]);
}
// result: [1,5,1]
This is your corrected code, just note that second array does not always need to contain three elements, you could loop through it as well with a for loop
The purpose is, it takes every value out of the array named "arr", compares it to a value of "newArr"...
You're not iterating over every value of arr, since you've newArr.length in your loop. You need to iterate till arr.length instead
...if the value doesn't match, push it into a new array.
Your code assumes newArr is always of length 3. Maybe it is. But, a more generic way is to use Array#indexOf which returns the first index at which a given element can be found in the array, or -1 if it is not present.
var arr = [1, 2, 3, 5, 1, 2, 3],
newArr = [2, 3, 3];
var finalArr = [];
for (var i = 0; i < arr.length; i++) {
if (newArr.indexOf(arr[i]) === -1) {
finalArr.push(arr[i]);
}
}
console.log(finalArr);

Move every other value from array into a new array

I have two one-dimensional arrays, a and b. a has values and b is empty. The length of a is an even number. I'd like to remove every other value from a and move them to b, in the same order as they were placed in a.
var a = [1, 2, 3, 4, 5, 6], b = [];
becomes
var a = [1, 3, 5], b = [2, 4, 6];
I figured that filter would do the trick but I'm not that happy with the performance of it since the average length of a is 300-400.
b = a.filter((i, idx) => {
return idx % 2 == 0;
});
a = a.filter((i, idx) => {
return idx % 2 == 1;
});
I've also been looking at lodash to see if that library had anything that might help me and the only function that's near what I'm looking for is _.chunk(array, \[size=1\]).
I appreciate any and all help to help me figure out a better, faster way to do this.
Since you mentioned lodash you could do this with _.partition:
let a = [1, 2, 3, 4, 5, 6];
let b = [];
let i = -1;
[a, b] = _.partition(a, (item) => i++ % 2);
console.log(a);
console.log(b);
<script src="https://cdn.jsdelivr.net/lodash/4.17.4/lodash.min.js"></script>
Partition's predicate is the identity function, which doesn't include the index of the item, so this comes with a compromise of an external index i.
Of course, you could always wrap this functionality into it's own function:
const splitEvenOdd = (array, i = -1) => _.partition(array, (item) => i++ % 2);
let a = [1, 2, 3, 4, 5, 6];
let b = [];
[a, b] = splitEvenOdd(a);
console.log(a);
console.log(b);
<script src="https://cdn.jsdelivr.net/lodash/4.17.4/lodash.min.js"></script>
Vanilla JS ES5, simple and clean.
var a = [1, 2, 3, 4, 5, 6], b = [];
for(var i = a.length-1; i >= 0; i--) {
if(i % 2 === 1) {
b.unshift(a.splice(i, 1)[0])
}
}
Basically, it is iterating through a backwards, and if the condition is true splicing the item und adding it as first item of b.
To loop through the source once, the values can be added to a specific array depending on the index. For example:
const source = [1, 2, 3, 4, 5, 6];
let arrs = [[],[]];
for(let i = 0; i< source.length; i++)
arrs[i%2].push(source[i]);
let [a,b] = arrs;
console.log(a);
console.log(b);
Alternatively, if it's important to alter the original arrays, a can be filled in a direct iteration, since the index being processed is always ahead of the one being filled:
let a = [1, 2, 3, 4, 5, 6], b= [];
for(let i = 0; i< a.length; i++)
(i % 2 ? b : a)[Math.floor(i/2)] = a[i];
a.splice(a.length/2);
console.log(a);
console.log(b);
The best performance you can get for this is 0(n) or linear time since you have to iterate through the entire array. What may help is reducing the number of loops
var a=[];
var b=[];
function splitArray(arr)
{
for (var i=0;i<arr.length;++i)
{
if (arr[i]%2 == 0)
b.push(arr[i]);
else
a.push(arr[i]);
}
}
What this does is reduces the number of times you have to iterate through the original array from 2 to 1

Comparing values in multiple arrays

I have two arrays where I need to compare values and get the duplicates. I wrote most of the code but seem to be stumped on the comparison.
Here is my code:
function compare(arr1, arr2) {
for (var i = 0; i< arr1.length; i++) {
for (var j = 0; j < arr2.length; j++) {
if (arr1[i] == arr2[j]) {
console.log[i];
}
}
}
}
compare([5, 3, 2, 5, 1, 6], [6, 4, 2, 7, 10]);
I get the for loops to print all of the numbers, but for some reason the if statement comparison doesn't work. Is there something I am not getting about comparing values in arrays?
I am not looking for a straight up answer but guidance if possible.
Your code is quadratic in time since it iterates the second array for each item in the first array. A linear time solution is to convert the first array into a hash table, and then, for each item in the second one, instantly check if it is in the hash.
function intersect(a, b) {
var hash = {};
a.forEach(function(x) { hash[x] = 1 });
return b.filter(function(x) { return hash[x] === 1 });
}
c = intersect([5, 3, 2, 5, 1, 6], [6, 4, 2, 7, 10]);
document.write(c)
Do note, however, that this only works if items to compare are primitives, you cannot put objects in a hash, so the code has to be quadratic:
function intersect(a, b) {
return a.filter(function(x) {
return b.indexOf(x) >= 0
});
}
a = {x:'a'};
b = {x:'b'};
c = {x:'c'};
d = {x:'d'};
i = intersect([a,b,c], [a,b,d]);
document.write(JSON.stringify(i));
Regarding your bit about improving your current code, I suggest that you make your javascript more idiomatic, in particular,
get used to iteration methods instead of for loops
check the repertoire of built-in functions and use them wherever possible
and, for sanity's sake, never ever use ==

Categories