I'm trying to write a recursive function that takes an array with nested arrays and puts all the values into a single array. Right now it works sometimes, but sometimes it doesn't.
function steamrollArray(arr) {
var newArr = [];
var func = function(array){
for(i=0; i<array.length; i++){
if(Array.isArray(array[i])){
func(array[i]);
}
else {
newArr.push(array[i]);
}
}
};
func(arr);
return newArr;
}
When I run steamrollArray([1, [2], [3, [[4]]]]) it works, but if I run steamrollArray([[1], [[2]], [3, [[4]]]]); it doesn't include the 2 for some reason, and if I run steamrollArray([1, [], [3, [[4]]]]) my browser crashes. Thanks for any insight you can give!
Your problem is the i is in the global scope, you need to declare it local, so add let i
function steamrollArray(arr) {
var newArr = [];
var func = function(array){
for(let i=0; i<array.length; i++){
if(Array.isArray(array[i])){
func(array[i]);
}
else {
newArr.push(array[i]);
}
}
};
func(arr);
return newArr;
}
console.log(steamrollArray([[1], [[2]], [3, [[4]]]]));
console.log(steamrollArray([1, [], [3, [[4]]]]));
Otherwise every time you call the function the i increases and you can go out of bond (like in your last example) or skipping some element (like in the second example)
Related
I can't figure out why this is not working. I know the issue comes from the 8th line in the condition if(arr[i][j]===elem).
function filteredArray(arr, elem) {
let newArr = [];
for (let i = 0; i < arr.length; i++) {
for(let j = 0; j < arr[i].length; j++) {
if(arr[i][j]===elem) {
newArr.push(arr.splice(i,1));
}
}
}
return newArr;
}
if I enter in my console a test like :
console.log(filteredArray([[3, 2, 3], [1, 6, 3], [3, 13, 26], [19, 3, 9]], 3));
I get the error arr[i] is undefined.
Your problem is that you are modifying the length of your array inside your loop. You have arr.splice(i,1) on the inside of your loop; every time that gets called, your array shortens by 1. Eventually, your array gets too short, and you attempt to read an item out of bounds. You can prove this by commenting out that line inside the if block; your code will run without errors.
I'm not sure exactly what values you're trying to put in newArr, but there's no reason to modify arr to get your values. Just read the value you want from arr, and push it to newArr, and you'll have no problems.
As an aside, is there any reason you have to use for loops vs. using native array methods like arr.filter()?
The task is:
You will be provided with an initial array (the first argument in the
destroyer function), followed by one or more arguments. Remove all
elements from the initial array that are of the same value as these
arguments.
While working through it I found some Array.filter behaviour I'm struggling to understand:
function destroyer(arr) {
for (var i = 1; i<arguments.length; i++){
toDelete = arguments[i];
arr.filter(isItIn);
}
return arr;
}
function isItIn(item, undefined, array){
return item!=toDelete;
}
destroyer([1, 2, 3, 1, 2, 3], 2, 3);
My intent here was to iterate through items 1+ of the arguments, calling arr.filter each time. Arr.filter then calls isItIn which checks if the currently examined item is the one I'm searching for. However, arr is being returned unchanged. When I change isItIn to:
function isItIn(item, undefined, array){
return item==1;
}
to test, it is still unchanged, however console.logs in the original writing of isItIn show that it is receiving the arguments correctly (or so far as I've thought to determine at least.
Please note, I've solved the problem through another route, I'm not looking for a solution to the problem, merely an explanation of where my initial code went wrong.
Basically you use Array#filter and omit the result of it.
You need to assign the result of filter to the former array.
arr = arr.filter(isItIn);
function destroyer(arr) {
for (var i = 1; i < arguments.length; i++) {
toDelete = arguments[i];
arr = arr.filter(isItIn);
}
return arr;
}
function isItIn(item) {
return item != toDelete;
}
console.log(destroyer([1, 2, 3, 1, 2, 3], 2, 3));
You have to assign the returned array:
function destroyer(arr) {
for (var i = 1; i<arguments.length; i++) {
toDelete = arguments[i];
arr = arr.filter(isItIn);
}
return arr;
}
I saw this function , though it works fine but I am bit puzzled about the function expressions. Here is the code
mapForEach(arr, fn) {
var newArr = [];
for (var i = 0; i < arr.length; i++) {
newArr.push(fn(arr[i]))
}
return newArr;
}
can anybody explain to nme what this rather complicated code is actually doing?
Lets say you have var array = [1, 2, 3, 5]; and then run var array2 = mapForEach(array, function(i) { return i * 2; })
array2 would then contain [2, 4, 6, 10].
So it returns a new array where you have the ability to modify each record with a function
mapForEach enumerates an array and calls a supplied function on each element.
example:
var a = [1, 2, 3];
console.log(mapForEach(a, (x) => x * 2));
would create a new array with the values (and output to console):
[2, 4, 6]
Basically it is an implementation of javascript native array function map, which creates a new array with the results of calling a provided function on every element in this array.
More info about mentioned function you can find here https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
I'm trying to solve a freeCodeCamp exercise with this goal:
Write a function that takes two or more arrays and returns a new array
of unique values in the order of the original provided arrays.
In other words, all values present from all arrays should be included
in their original order, but with no duplicates in the final array.
The unique numbers should be sorted by their original order, but the
final array should not be sorted in numerical order.
So what I do is concatenate all the arguments into a single array called everything. I then search the array for duplicates, then search the arguments for these duplicates and .splice() them out.
So far everything works as expected, but the last number of the last argument does not get removed and I can't really figure out why.
Can anybody please point out what I'm doing wrong? Please keep in mind that I'm trying to learn, so obvious things probably won't be obvious to me and need to be pointed out. Thanks in advance.
function unite(arr1, arr2, arr3) {
var everything = [];
//concat all arrays except the first one
for(var x = 0; x < arguments.length; x++) {
for(var y = 0; y < arguments[x].length; y++) {
everything.push(arguments[x][y]);
}
}
//function that returns duplicates
function returnUnique(arr) {
return arr.reduce(function(dupes, val, i) {
if (arr.indexOf(val) !== i && dupes.indexOf(val) === -1) {
dupes.push(val);
}
return dupes;
}, []);
}
//return duplicates
var dupes = returnUnique(everything);
//remove duplicates from all arguments except the first one
for(var n = 1; n < arguments.length; n++) {
for(var m = 0; m < dupes.length; m++) {
if(arguments[n].hasOwnProperty(dupes[m])) {
arguments[n].splice(arguments[n].indexOf(dupes[m]), 1);
}
}
}
//return concatenation of the reduced arguments
return arr1.concat(arr2).concat(arr3);
}
//this returns [1, 3, 2, 5, 4, 2]
unite([1, 3, 2], [5, 2, 1, 4], [2, 1]);
Looks like you overcomplicated it a bit ;)
function unite() {
return [].concat.apply([], arguments).filter(function(elem, index, self) {
return self.indexOf(elem) === index;
});
}
res = unite([1, 2, 3], [5, 2, 1, 4], [2, 1], [6, 7, 8]);
document.write('<pre>'+JSON.stringify(res));
Explanations
We split the problem into two steps:
combine arguments into one big array
remove non-unique elements from this big array
This part handles the first step:
[].concat.apply([], arguments)
The built-in method someArray.concat(array1, array2 etc) appends given arrays to the target. For example,
[1,2,3].concat([4,5],[6],[7,8]) == [1,2,3,4,5,6,7,8]
If our function had fixed arguments, we could call concat directly:
function unite(array1, array2, array3) {
var combined = [].concat(array1, array2, array3);
// or
var combined = array1.concat(array2, array3);
but as we don't know how many args we're going to receive, we have to use apply.
someFunction.apply(thisObject, [arg1, arg2, etc])
is the same as
thisObject.someFunction(arg1, arg2, etc)
so the above line
var combined = [].concat(array1, array2, array3);
can be written as
var combined = concat.apply([], [array1, array2, array3]);
or simply
var combined = concat.apply([], arguments);
where arguments is a special array-like object that contains all function arguments (actual parameters).
Actually, last two lines are not going to work, because concat isn't a plain function, it's a method of Array objects and therefore a member of Array.prototype structure. We have to tell the JS engine where to find concat. We can use Array.prototype directly:
var combined = Array.prototype.concat.apply([], arguments);
or create a new, unrelated, array object and pull concat from there:
var combined = [].concat.apply([], arguments);
This prototype method is slightly more efficient (since we're not creating a dummy object), but also more verbose.
Anyways, the first step is now complete. To eliminate duplicates, we use the following method:
combined.filter(function(elem, index) {
return combined.indexOf(elem) === index;
})
For explanations and alternatives see this post.
Finally, we get rid of the temporary variable (combined) and chain "combine" and "dedupe" calls together:
return [].concat.apply([], arguments).filter(function(elem, index, self) {
return self.indexOf(elem) === index;
});
using the 3rd argument ("this array") of filter because we don't have a variable anymore.
Simple, isn't it? ;) Let us know if you have questions.
Finally, a small exercise if you're interested:
Write combine and dedupe as separate functions. Create a function compose that takes two functions a and b and returns a new function that runs these functions in reverse order, so that compose(a,b)(argument) will be the same as b(a(argument)). Replace the above definition of unite with unite = compose(combine, dedupe) and make sure it works exactly the same.
You can also try this :
var Data = [[1, 2, 3], [5, 2, 1, 4], [2, 1], [6, 7, 8]]
var UniqueValues = []
for (var i = 0; i < Data.length; i++) {
UniqueValues = [...new Set(UniqueValues.concat(Data[i]))]
}
console.log(UniqueValues)
Couldn't really find an answer for this.
I have a function which should allow users to pass it "checks" (functions that returns true or false). The checks will run on a large number of items. For each item I want to know if all the checks returned true.
function foo(checksArray) { //checksArray: [func1, func2, func3]
var itemList = [1, 2, 3, 4];
for (item of itemList)
if (checkAllFunctions(item))
doSomething();
}
How can I do it? Obviously I can iterate over each function with a for loop but I suspect there might be a better way. Maybe there's even a one-liner.
Thanks for any help guys.
Edit: I guess there isn't really any point in keeping running even though one of checks returned false. If it can stop right there, that's even better!
Use forEach to take up each element of itemList. Inside that loop, use every to check if every function in checksArray passes.
function foo(checksArray) { //checksArray: [func1, func2, func3]
var itemList = [1, 2, 3, 4];
itemList.forEach(function(item) {
if (checksArray.every(function(check) { return check(item); })) doSomething();
});
}
You can use simple flag for that. Try something like this:
function foo(checksArray) { //checksArray: [func1, func2, func3]
var itemList = [1, 2, 3, 4];
var flag = true;
for (item of itemList)
flag = checkElement(item); // check single element, not all
if(flag){
// here you can check your state after every single item
}
}
if(flag){
doSomething();
}
}
Not necessarily shorter, but cleaner and correct:
function foo (checksArray) {
var itemList = [1, 2, 3, 4];
var meetsAllCriteria = itemList.every(function (item) {
return checkAllFunctions(item);
});
if (meetsAllCriteria) {
doSomething();
}
}