I am trying to iterate an array with forEach and, based on a condition, I do something.
For this question, I simplified the condition in order to try to understand what's going on.
I expect array b = [] after the operation, but it is not, as it operates only on half on the elements. Why does this happen?
(This is not about removing everything from b, just trying to understand why it jumps the even indexes).
var a = [1, 2, 3, 4, 5, 6];
var b = a.slice(0);
console.log('before b = ', b); // b = [1, 2, 3, 4, 5, 6]
a.forEach(function (e) {
if (e > 0) {
b.splice(a.indexOf(e), 1);
}
});
console.log('after b = ', b); // b = [2, 4, 6]
// but I expect b = []
It does not. It goes through each and every item. The thing is what you do with array b.
First you remove the index 0 from it which is 1. So now b = [2,3,4,5,6].
Then index 1 which is 3 so b = [2,4,5,6].
Then index 2 which is 5 so b = [2,4,6].
The next indexes don't exist so in the end b = [2,4,6].
To have the expected outcome use b.indexOf(e) in your splice call.
var a = [1, 2, 3, 4, 5, 6];
var b = a.slice(0);
console.log('before b = ', b); // b = [1, 2, 3, 4, 5, 6]
a.forEach(function (e) {
if (e > 0) {
b.splice(b.indexOf(e), 1);
}
});
console.log('after b = ', b); // b = [2, 4, 6]
// but I expect b = []
You could take the index directly for splicing and make an log from the value an the array. You se, that the array becomes smaller than the index for splicing.
var a = [1, 2, 3, 4, 5, 6];
var b = a.slice(0);
console.log(b.join(' '));
a.forEach(function (e, i) {
if (e > 0) {
b.splice(a.indexOf(e), 1);
console.log(i, ': ', b.join(' '));
}
});
console.log(b); // b = [2, 4, 6]
To empty b, you need to look for the index of the array b, not a and splice it.
var a = [1, 2, 3, 4, 5, 6];
var b = a.slice(0);
a.forEach(function (e, i) {
if (e > 0) {
b.splice(b.indexOf(e), 1);
// ^
}
});
console.log(b);
You're splicing based on the index of the element from a, however b's indices are getting updated when you splice in it. You should try to splice from b based on the index of the element IN b.
Related
I am answering this exercise in FreeCodeCamp here's the instruction
Drop it
Given the array arr, iterate through and remove each element starting from the first element (the 0 index) until the function func returns true when the iterated element is passed through it.
Then return the rest of the array once the condition is satisfied, otherwise, arr should be returned as an empty array.
this is what I have so far. I used the map function to iterate the indexes of array then check if it met the condition of the function parameter inside the map. In this case, the function should return only the numbers less than 3 but I can't get rid of 3 when returning it.
function dropElements(arr, func) {
return arr.map(x => func(x) ? x : "");
}
console.log(dropElements([1, 2, 3], function(n) {return n < 3; }));
You could take a closure over a boolean value and keep a true value for the rest of the array for filtering.
Source of data/results: https://www.freecodecamp.org/learn/javascript-algorithms-and-data-structures/intermediate-algorithm-scripting/drop-it
function dropElements(arr, func) {
return arr.filter((b => v => b ||= func(v))(false));
}
console.log(...dropElements([1, 2, 3], function(n) { return n < 3; })); // [1, 2, 3]
console.log(...dropElements([1, 2, 3, 4], function(n) { return n >= 3; })); // [3, 4]
console.log(...dropElements([0, 1, 0, 1], function(n) { return n === 1; })); // [1, 0, 1]
console.log(...dropElements([1, 2, 3], function(n) { return n > 0; })); // [1, 2, 3]
console.log(...dropElements([1, 2, 3, 4], function(n) { return n > 5; })); // []
console.log(...dropElements([1, 2, 3, 7, 4], function(n) { return n > 3; })); // [7, 4]
console.log(...dropElements([1, 2, 3, 9, 2], function(n) { return n > 2; })); // [3, 9, 2]
Would suggest using a filter, which returns another array:
function dropElements(arr) {
return arr.filter(x => x < 3);
}
const someArray = [1, 2, 3];
console.log(dropElements(someArray)); // [1, 2]
It says "remove elements" so I'm gonna mutate the array that was passed in.
function dropElements(arr, func) {
while (arr.length && !func(arr[0])) {
arr.splice(0, 1);
}
return arr;
}
console.log(dropElements([1, 2, 3], function(n) {return n < 3; }));
Note: Not a duplicate problem.. here I need to skip empty arrays.
Say I have several arrays like:
var a = [1, 2, 3, 4],
b = [2, 4],
c = [],
d = [4];
Using following function, I could get the desired result: [4]
var a = [1, 2, 3, 4],
b = [2, 4],
c = [],
d = [4];
var res = [a, b, c, d].reduce((previous, current) =>
!previous.length || previous.filter((x) => !current.length || current.includes(x)),
);
console.log(res)
I included !current.length || above to bypass empty array c. But this doesn't work if first array in the collection i.e. a is empty. The result would be [].
This code will work as you expected (vanilla JS, support old browsers):
var a = [1, 2, 3, 4],
b = [2, 4],
c = [],
d = [4];
var res = [a, b, c, d].reduce(function(acc, arr) {
// ignore empty array
if(arr.length == 0) return acc;
// assign first non-empty array to accumudation
if(acc.length == 0) return arr;
// otherwise, accumudation will be insection of current accomudation and current array
return acc.filter(function(n) {
return arr.indexOf(n) !== -1;
});
}, []);
console.log(res)
Just filter. Makes the code much more readable
var a = [1, 2, 3, 4],
b = [2, 4],
c = [],
d = [4];
var res = [c, b, a, d].filter(arr => arr.length).reduce((previous, current) =>
previous.filter((x) => current.includes(x)),
);
console.log(res)
How can I check if two integers arrays are permutations? I need to do it in JavaScript.
For example, I have two arrays:
a = [1, 2, 3, 4, 5]
and
b = [2, 3, 5, 1, 4]
I need the program to return true.
You could use a Map to store the occurrence count and then decrease that count whenever you find a mapping occurrence in the other array:
function isPermutation(a, b) {
if (a.length !== b.length) return false;
let map = new Map(a.map(x => [x, { count: 0 }]));
a.forEach(x => map.get(x).count++);
return b.every(x => {
let match = map.get(x);
return match && match.count--;
});
}
let a =[1,2,3,4,5,1];
let b = [2,3,1,5,1,4];
console.log(isPermutation(a, b));
The lazy solution:
let a = [1,2,3,4,5],
b = [2,3,5,1,4];
let res = JSON.stringify(a.sort()) === JSON.stringify(b.sort())
console.log(res)
The more efficient solution:
function perm (a,b) {
let map = a.reduce((acc,c) => {acc[c] = (acc[c] || 0) + 1; return acc},{})
for (el of b) {
if (!map[el] || map[el] == 0) {
return false;
} else {
map[el]--;
}
}
for (k in map) {
if (map[k] != 0) {
return false;
}
}
return true;
}
console.log(perm([1, 2, 3, 4, 5],[2, 3, 5, 1, 4])) // => true
console.log(perm([1, 2, 3, 4, 5, 5, 5],[2, 3, 5, 1, 4])) // => false
console.log(perm([1,1,2],[1,2,2])) // => false
console.log(perm([1,2,3,4,5,1],[2,3,1,5,1,4])) // => true
This solution is in hindsight similar to the one of #trincot but I guess slightly different enough to keep it posted.
The idea is the following: We create a map from the first array via reduce where the value is a count of occurrences. We then take the other array and subtract occurrences from the respective keys of map. If the key doesn't exist is or the value is already zero, we know this is not a permutation. Afterwords we loop the map, checking whether all values are exactly zero.
var a = [1, 2, 3, 4, 5];
var b = [2, 3, 5, 1, 4];
return a.filter(x => !b.includes(x)).length === 0
This will return true if all of the values in a exists in b, regardless of position.
This worked:
var a =[1,2,3,4,5,1];
var b = [2,3,1,5,1,4];
console.log(a.sort().toString() === b.sort().toString())
The following was my interview question. But I couldn't crack it and even could not think how to get this done.
var arr = [1,4,5,8,3,2,6,9,7,10];
Expected output of alternate sorting:
[10,1,9,2,8,3,7,4,6,5]
What I have tried:
I tried slicing out the Math.max.apply(null,arr) and Math.min.apply(null,arr) alternatively to push into separate empty array. But It was told that the algorithm is not optimal.
I would sort the array, and then iterate it, picking values from the begining and the end (inloop calculated offsets), in each iteration. A final check to odd arrays would complete the process.
let a = [1, 4, 5, 8, 3, 2, 6, 9, 7, 10];
a.sort((a, b) => a - b);
let b =[];
let l = a.length-1; // micro optimization
let L = l/2; // micro optimization
for(var i=0; i<L; i++) b.push( a[l-i] ,a[i] );
if(a.length%2) b.push( a[i] ); // add last item in odd arrays
console.log(b);
Result :
b = [10, 1, 9, 2, 8, 3, 7, 4, 6, 5]
Algorithm bennefits:
Avoiding alterations in the original array (through pop and shift), improves the performance considerably.
Precalculating l and L before the loop , prevents the need of being calculated repeatedly in each iteration.
A single conditional cheking at the end of the procces, to handle odd arrays, slightly improves the speed.
I've prepared some PERFORMANCE TESTS, with some of the proposed algorithms :
Original Array(10 items) and Big Array(1000 items)
Here is one way to do it:
var arr = [1, 4, 5, 8, 3, 2, 6, 9, 7, 10];
// Sort the source array
arr.sort((a, b) => a - b);
// This will be the final result
var result = [];
// Create two pointers
var a = 0,
b = arr.length - 1;
while (result.length < arr.length) {
// Push the elements from start and end to the result array
result.push(arr[b]);
// Avoid bug when array is odd lengthed
if (a !== b) {
result.push(arr[a]);
}
a++;
b--;
}
console.log(result);
The idea is to have two pointers (a and b) traversing the the sorted original array from both the directions and appending the elements in result.
If you assume that the array will be a set of sequential numbers (a good question to ask about the data) you can do this very quickly with no need to sort or mutate the original array(i.e O(n)):
var arr = [1, 4, 5, 8, 3, 2, 6, 9, 7, 10];
let a = arr.reduce((a, c, i) => {
a[c > arr.length >> 1 ? (arr.length - c) << 1 : (c << 1) - 1] = c
return a
}, [])
console.log(a)
Here's my answer, based off the intuition that you're taking from the front then the back repeatedly from the sorted array until you're empty. The trick is avoiding "max" and "min" which evaluate the entire array, and just sorting it once.
Many of the other answers will put an undefined into the array if the original array has an odd length. I would leave a comment on those but I do not have the reputation. This is why I bounds check twice per loop.
var arr = [1,4,5,8,3,2,6,9,7,10];
// Sort numerically (not lexicographically)
arr.sort((a, b) => a - b)
// The output array
var out = []
// Take from the front, then back until original array is empty
while (true) {
if (arr.length == 0) break
out.push(arr.pop())
if (arr.length == 0) break
out.push(arr.shift())
}
// Output answer
console.log(out)
My solution for readability / no hidden magic:
// Input
var arr = [1,4,5,8,3,2,6,9,7,10];
// Sort
var arr1 = arr.sort((a,b) => (a - b));
// Compose
var arr2 = [];
for (var i = 0; i < arr1.length; i++) {
arr2.push(arr1[(i % 2) === 0
? arr1.length-1-(i/2) // get from end half
: (i-1)/2 // get from begin half
])
}
// Output
console.log(arr2); // = [10, 1, 9, 2, 8, 3, 7, 4, 6, 5]
Their interview answer "that the algorithm is not optimal." is not unexpected ofcourse. I would inquire why they say that, and ask if its really benefitial to spend dollar time on dimes here. (or tens of dollars on cents, actually)
Alternative method with only one variable to increment:
var arr = [1, 4, 5, 8, 3, 2, 6, 9, 7, 10];
arr = arr.sort((a, b) => b - a);
var result = [];
var a = 0;
while (result.length < arr.length) {
result.push(arr[a]);
result.push(arr[arr.length - a - 1]);
a++;
}
console.log(result);
var a = [1,4,5,8,3,2,6,9,7,10];
var b = a.sort((a, b) => a - b);
var c = a.sort((a, b) => a - b).reverse();
var d = [];
let e = a.length-1;
let f = e/2;
for(let i=0; i<f; i++) d.push( b.pop(), c.pop() );
Replace b and c in the for loop with functions to test:
for(let i=0; i<f; i++) d.push( a.sort((a, b) => a - b).pop(), a.sort((a, b) => a - b).reverse().pop() );
sort the array and divide into two parts , now use reduce to put elements from the two arrays
//original array
var arr = [1, 4, 5, 8, 3, 2, 6, 9, 7, 10];
//sorting origina array in ascending order
var m = arr.sort(function(a, b) {
return a - b;
});
// diving the sorted array in two parts
let getFirstSet = m.splice(0, arr.length / 2);
// now m containleft over items after the splice
// again sorted it in descending order to avoid back looping
let getSecondSet = m.sort(function(a, b) {
return b - a;
});
//using reduce function
let newArray = getFirstSet.reduce(function(acc, curr, index) {
// pushing element from second array containing 10,9,8,7,6
acc.push(getSecondSet[index]);
// pushing element from first array containing 1,2,3,4,5
acc.push(getFirstSet[index]);
return acc;
}, []); // [] is the initial array where elements will be pushed
console.log(newArray)
Another alternative view ... should this funky sort be done in place, like .sort is?
let input = [1, 4, 5, 8, 3, 2, 6, 9, 7, 10];
input.sort((a, b) => b - a).every((n, i, a) => (a.splice((i * 2 + 1), 0, a.pop()), (i * 2) < a.length));
console.log(input);
Here is a quick solution, using ternary operators and modulo operator for toggling.
let arr = [1, 4, 5, 8, 3, 2, 6, 9, 7, 10];
let j = 0;
let k = arr.length - 1;
// sort array
arr.sort((a, b) => a - b);
let new_array = [];
for (let i in arr) {
new_array[i] = i % 2 == 0 ? arr[k--] : arr[j++];
}
// prints array
console.log(new_array);
function destroyer(arr) {
var arry=[];
for(var i=1;i<arr.length;i++)
{
arr[0] = arr[0].filter(cc => cc != arr[i]);
}
return arry;
}
destroyer([1, 2, 3, 1, 2, 3], 2, 3);
I basically have to return all the elements of the first sub-array which arent present in the rest of array.
Its displaying "arr[0].filter isnt a function.
destroyer([1, 2, 3, 1, 2, 3], 2, 3) should return [1, 1].
I basically have to return all the elements of the first sub-array which arent present in the array.
You aren't doing anything with the other arguments provided to the destroyer function - you have to test those arguments against arr, you shouldn't be testing arr against itself.
function destroyer() {
const [arr, ...excludeArr] = arguments;
return arr.filter(elm => !excludeArr.includes(elm));
}
console.log(
destroyer([1, 2, 3, 1, 2, 3], 2, 3)
);
function destroyer(arr, x, y) {
return arr.filter(item => (item != x) && (item != y))
}
console.log(destroyer([1, 2, 3, 1, 2, 3], 2, 3))
const destroyer = (arr, ...args) => {
return arr.filter(item => args.indexOf(item) < 0);
};
const result = destroyer([1, 2, 3, 1, 2, 3], 2, 3);
console.log(result);
as you said you should filter first subarray but you are sending only 1 array with int values and other values as argument, the actual usage of this function should be
function destroyer(arr) {
for(var i = 1; i < arr.length; i++)
{
arr[0] = arr[0].filter(cc => cc != arr[i]);
}
return arr[0];
}
destroyer([[1, 2, 3, 1, 2, 3], 2, 3]);
You forgot to call your method using the array. you have to surround it with another pair of [].
You can simply return the filtered arr[0] to get what you want.
function destroyer(arr) {
for (var i = 1; i < arr.length; i++) {
arr[0] = arr[0].filter(cc => cc != arr[i]);
}
return arr[0];
}
console.log(destroyer([[1, 2, 3, 1, 2, 3], 2, 3]));