I am trying to delete out the common elements in arguments given and
create a final array with unique elements.
In the below example my final array is giving me [1,3,4,5] instead of [1,4,5].
I did the debug and the one of element (3) is not going through the loop.
After element (2) it's skipping to element (4). I am confused why is this happening? Anything wrong with my code? Any help appreciated.
Thank you
function arrayBreaker(arr) {
let test = [];
test.push(arguments[1]);
test.push(arguments[2]);
let target = arguments[0];
target.filter(val => {
if (test.indexOf(val) > -1) {
target.splice(target.indexOf(val), 1);
}
});
return target;
}
console.log(arrayBreaker([1,2,3,4,5], 2, 3));
You're using .filter() like a general purpose iterator, and mutating the array you're iterating, which causes problems because the iterator is unaware of the items you're removing.
If you need to mutate the original array, instead return the result of the .indexOf() operation from the .filter() callback, or better yet, use .includes() instead, and then copy the result into the original after first clearing it.
function arrayBreaker(target, ...test) {
const res = target.filter(val => !test.includes(val));
target.length = 0;
target.push(...res);
return target;
}
console.log(arrayBreaker([1, 2, 3, 4, 5], 2, 3));
If you don't need to mutate, but can return a copy, then it's simpler.
function arrayBreaker(target, ...test) {
return target.filter(val => !test.includes(val));
}
console.log(arrayBreaker([1, 2, 3, 4, 5], 2, 3));
Array is returning a new array based on filter criteria, in this case you are checking index of test. In the callback function is expecting a true if you want to keep the value, otherwise return false. In your case you shouldn't slice the target inside filter callback, but rather:
function arrayBreaker(arr) {
let test = [];
test.push(arguments[1]);
test.push(arguments[2]);
let target = arguments[0];
let newArray = target.filter(val => {
if (test.indexOf(val) > -1) {
// filter value out
return false;
}
// keep the val inside new array
return true;
});
return newArray;
}
console.log(arrayBreaker([1,2,3,4,5], 2, 3));
for more information about array filter, check documentation here:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
Depending on your use case there are few things that I think would be helpful, first I would declare the function parameters so it's clear that arrayBreaker is expecting multiple arguments. Secondly I would expect second param as an array since we are expecting multiple values. Something like below:
function arrayBreaker(arr, filterValues) {
return arr.filter(val => {
if (filterValues.indexOf(val) > -1) return false;
return true;
})
}
console.log(arrayBreaker([1,2,3,4,5], [2, 3]));
Related
I would like to check if an element inside an array is another array. I'm solving a code challenge where the problem is iterating through an array and checking for a 7 but if an
element is an array I would like to continuously check each nested array for a 7.
I have console.log() in my first 'if' statement and I've seen that sevenBoom() is being called more than once. But for some reason it's not returning 'Boom!'
SevenBoom should return 'Boom!' if there's a seven.
function sevenBoom(arr) {
if (arr.includes(7)) {
return "Boom!";
}
arr.forEach((val) => {
if (Array.isArray(val)) sevenBoom(val);
});
}
sevenBoom([1, 3, 4, 6, [7]) // Returns undefined
sevenBoom([3, 7, 8, 9]) // Returns 'Boom!'
You could take a boolean value as result and use Array#some for checking arrays.
function hasSeven(array) {
function seven(a) {
return a.includes(7) || a.some(v => Array.isArray(v) && seven(v));
}
return seven(array)
? 'Boom!'
: 'There is no 7';
}
console.log(hasSeven([7]));
console.log(hasSeven([[7]]));
console.log(hasSeven([[[7]]]));
console.log(hasSeven([]));
console.log(hasSeven([[]]));
console.log(hasSeven([[[]]]));
const sevenBoom = (arr) => {
const recurse = (arr) => arr.some(n => Array.isArray(n) ? recurse(n) : n === 7);
if (recurse(arr)) {
return 'Boom';
}
}
This is assuming what should be returned other than 'Boom' is void. It's a bit of an awkward place for recursion since you want to return a string if you meet some criteria and nothing otherwise.
Firstly you need to return the value from your second if condition too.
But forEach() cannot be interrupted (for ex: with a return statement) and will run for all items. So you can keep track using a flag variable outside the forEach() and return your result on that basis.
function sevenBoom(arr) {
if (arr.includes(7)) {
return "Boom!";
}
let found = false;
arr.forEach((val) => {
if (Array.isArray(val)) if(sevenBoom(val)) found="Boom!";
})
return found;
}
console.log(sevenBoom([1,2,3]));
console.log(sevenBoom([1,2,7]));
console.log(sevenBoom([1,2,[2,7]]));
console.log(sevenBoom([1,2,[2,3,[4,5]]]));
console.log(sevenBoom([1,2,[2,3,[4,7]]]));
Note: How sevenBoom() can be directly used inside an if statement. This is because of the concept of truthy and falsy values.
PS: As mentioned above, forEach() will run for all items, no matter what. You can use any other looping mechanism like a simple for loop, some() etc. I just copied your code and hence used forEach()
I would check if an element is seven in the same loop that you are checking if an element is an array that way you can avoid going through the array unnecessarily.
const sevenBoom = arr => {
for (const ele of arr) {
if (ele === 7) return 'Boom';
if (Array.isArray(ele)) {
//this prevents you from halting your function early
//you only want to return in the loop if a 7 is found
if (boom(ele) === 'Boom') return 'boom'
}
}
}
You can use the .flat(depth) method to flatten each array before using the .includes() method. Choose a depth that would cover all possible arrays for your project.
function sevenBoom(arr) {
return arr.flat(10).includes(7) ? 'Boom!' : '-'
}
DEMO
let a = [1, 3, 4, 6, [7]],
b = [3, 7, 8, 9],
c = [1,2,[5,6,[3,5,[7,6]]]],
d = [0],
e = [1, [5, 4], 3, 5, [7]];
function sevenBoom(arr) {
return arr.flat(10).includes(7) ? 'Boom!' : '-'
}
for(let arr of [a,b,c,d,e]) {
console.log( sevenBoom( arr ) );
}
console.log( e ); //Original array remains unchanged
If 7 is not present in the root array, then your function isn't returning anything.
Try this, just a minor refactoring:
function sevenBoom(arr) {
if (arr.includes(7)) return 'Boom!';
for (let val of arr) {
if (Array.isArray(val)) {
if (sevenBoom(val)) return sevenBoom(val);
}
}
return false;
}
function twoSum(numbers, target) {
var result = [];
numbers.forEach(function(value, index) {
return numbers.forEach(function(value2, index2) {
if (value + value2 === target) {
result.push(index, index2);
return result;
}
})
})
return result;
}
twoSum([1, 2, 3], 4);
//Output - [ 0, 2, 1, 1, 2, 0 ]
Hi - I'm working on a particular codewars problem and I seem to be misunderstanding the usage of return for callback functions. In this particular problem I just want to find the first two sums of numbers that equal the target and push those index values into result. I don't want to keep iterating through my function after that - meaning I only want the first pair that's found. My current output gives me all the index values for the target sum. Not just the first 2. It seems I am not using my return commands correctly. My current line of thought is that return result returns a value to my nested callback of parameters (value2, index2). That result is then returned to my outside function of (value,index). Why does my loop not cease after that return?
It doesn't end because .forEach cannot be terminated early. forEach is not paying any attention to the values you return. If you want to terminate early you'll need to use a different approach.
If you want to stick with array methods, there are .some and .every. The former continues until a run of your function returns true, and the latter continues until a run of your function returns false. These are meant for doing compound OR's and compound AND's with every element of the array, but they can kinda be used for your case too.
numbers.some(function(value, index) {
return numbers.some(function(value2, index2) {
if (value + value2 === target) {
result.push(index, index2);
return true;
}
return false;
})
})
Or you could use a standard for loop, with the break keyword when you want to stop the loop.
Beside the not working return statement for a outer function, you need to take a different approach which uses only a single loop and an object for storing the index of the found value.
function twoSum(numbers, target) {
var indices = {};
for (let i = 0; i < numbers.length; i++) {
const number = numbers[i];
if (number in indices) return [indices[number], i];
indices[target - number] = i;
}
}
console.log(twoSum([1, 2, 3], 4));
I currently have a function that allows me to test if something a piece (for connect 4) is in an array, as well as 1, 2, and less respectively. If all 4 numbers are in the array are present, then it returns true. This works.
What I am trying to do is make it so I can use .some, so I can test if the array contains any cases of having a number, and again 3, 2, and 1 less than the number tested.
Right now it will test an individual piece, but I don't know how to get it to grab onto the array to check the index of the individual element it is testing.
Thank you for any responses/Ideas.
const testPieces = [1, 2, 3, 4]
const fourInARow = function(piece, array) {
for (var i = piece; i >= piece - 3; i--) {
if (array.indexOf(i) === -1) {
return false
}
}
return true
}
testPieces.some(fourInARow) // The piece that I don't know how to make work
Calling .some on your testPieces array will pass in each element of the array to the fourInARow function as the piece argument. It will not pass in your second array argument.
You need to provide a function to the .some call that already knows about the array for testing. You can do this by returning a function from the function e.g.
const fourInARow = function(array) {
return function(piece) {
for (var i = piece; i >= piece - 3; i--) {
if (array.indexOf(i) === -1) {
return false
}
}
return true
};
}
The array you are testing can now be passed to the .some call like this;
testPieces.some(fourInARow([1,2]));
The returned function has created a closure which retains a reference to the test array [1,2] which is then compared to the piece argument supplied by the call to .some.
Just wondering why not flip the logic to start from the other side and use instead of some every with includes?
const testPieces = [1, 2, 3, 4]
const inARow = (arr, base) => arr.every((x) => base.includes(x))
console.log(inARow([4,3,1,2], testPieces))
console.log(inARow([5,2,1], testPieces))
It becomes one line, it does not care about the order etc. Let me know if I am missing something ...
I am trying to use the reduce method as follows to eliminate duplicates however, it is not quite working:
var unique = function(array) {
array = array.sort(function(a,b) {return a-b;});
var noDup = [array[0]];
array.reduce(function(c,d) {
if(c!==d) {
noDup.push(d);
return d;
}
});
return noDup;
};
var x = [9,2,1,5,9,1,1,4,2,9];//==>[1, 1, 2, 4, 5, 9, 9]
function unique(values) {
return values.reduce(function(prev, cur) {
if (prev.indexOf(cur) == -1) {
prev.push(cur);
}
return prev;
}, []);
}
unique([9,2,1,5,9,1,1,4,2,9]) // --> [9, 2, 1, 5, 4]
fiddle
You are using the "intermediate value" of reduce to hold the previous value, so you can check against it the next time through. But that leaves you with no way to calculate the real intermediate value you want, which is the unique array you are building, so you having to declare it outside (noDup), which sort of defeats the whole purpose. Then your code has issues such as not providing an initial value to reduce. In that case, reduce has a special behavior which is that it calls the callback with the first two values of the array; a situation you are not handling properly.
Anyway, since it seems you are willing to sort the array, you can avoid doing an indexOf each time through the loop, by just remembering the previous value and checking against it:
function unique(values) {
var prev;
return values . sort() . reduce(function(result, cur) {
if (cur !== prev) result.push(cur);
prev = cur;
return result;
}, []);
}
But it turns out actually we don't need to keep the value of prev; instead, we can simply refer to the previous element directly, since filter passes additional arguments of index and array to the callback, so:
function unique(values) {
return values . sort() . reduce(function(result, cur, index, array) {
if (cur !== array[index-1]) result.push(cur);
return result;
}, []);
}
But if you think about it, this is nothing more than a filter written using reduce. It's just filtering out numbers that are the same as the previous one. So just write it as a filter to start with:
function unique(values) {
return values . sort() . filter(value, i, arr) { return value !== arr[i-1]; });
}
There are other approaches to removing duplicates using filter which don't require the sort. Here's a simple one:
values . filter(function(value, i, arr) { return arr.indexOf(value) === i; });
What this says is, filter out a number if the location where it is first found in the array is its location. In other words, filter out numbers that occur earlier in the array.
I found a solution to where I get returned an array of elements without duplicates:
Array1 = Array1.filter(function(val) {
return Array2.indexOf(val) == -1;
});
However, I want to modify this code just a little bit. Instead of being returned an array without duplicates, I want to do something when there is a duplicate. The problem is, I'm not sure how exactly this code works. The thing is I'm not sure how val gets set, or what it even is.
for (var i = 0; i < json.length; i++) {
var item = json[i];
// if json.indexOf(val?), do something
}
Read the docs for the Array filter method then. The val parameter of the callback will be passed the single array items, i.e. json[i] or item in your case:
for (var i = 0; i < json.length; i++) {
var item = json[i];
if (json.indexOf(item) >= 0) {
// do something
}
}
var newArray = array1.filter(function(v, i) {
return array1.indexOf(v) == i;
});
This will return only unique itesm from array1;
array1.filter(function(v, i) {
// write your code here ('v' is individual value and 'i' is its index)
// don't return any anything if you don't want unique array to be returned.
// 'array1.indexOf(v) == i' checks if current value is duplicate from previous any values.
// try putting console.log on values you don't understand like (console.log(v,i) for values of 'v' and 'i')
return array1.indexOf(v) == i;
});
and off-curse you can loop an array with for loop as
for(i in array1){
// where i is index of array1, to get current value use array1[i]
if(array2.indexOf(array1[i]) >= 0){
// do something
}
console.log(i);
}
val is set by Array.prototype.filter, which calls the callback function on each element in the array. Since you don't want to filter you can use Array.prototype.forEach instead, which also calls the callback function once for each element in the array:
Array1.forEach(
// This function is called once per element in Array1
function(val){
if(Array2.indexOf(val) != -1){ // Check if that element is also in Array2
// `val` is in both arrays,
// Do something with it
}
}
);
You can utilize some modern libraries... like underscorejs.
Intersection is what you're looking for i guess: http://underscorejs.org/#intersection
_.intersection([1, 2, 3], [101, 2, 1, 10], [2, 1]);
=> [1, 2]
So your code may be something like
if(_.insersection(arr1, arr2)){
//since [] array is Falsy in JS this will work as a charm
}
From MDN: indexOf
Returns the first index at which a given element can be found in the array, or -1 if it is not present.
From MDN: filter
Creates a new array with all elements that pass the test implemented by the provided function.
The first function works by returning true when an item from array1 isn't found in array2 (== -1). i.e.: Iterate through A and add anything not found in B.
So, to change to return only duplicates return true for anything that is found in both:
Array1 = Array1.filter(function(val) {
return Array2.indexOf(val) >= 0;
});
Array1 now contains only items with duplicates.