Flattening an array recursively (withoout looping) javascript - javascript

I'm practicing recursion and am trying to flatten an array without looping (recursion only). As a first step I did the iterative approach and it worked, but am stuck on the pure recursion version:
function flattenRecursive(arr) {
if (arr.length === 1) {
return arr;
}
return Array.isArray(arr) ? arr = arr.concat(flattenRecursive(arr)) : flattenRecursive(arr.slice(1))
}
console.log(flattenRecursive([
[2, 7],
[8, 3],
[1, 4], 7
])) //should return [2,7,8,3,1,4,7] but isn't - maximum call stack error
//working version (thanks #Dave!):
function flattenRecursive(arr) {
if (arr.length === 1) {
return arr;
}
return arr[0].concat(Array.isArray(arr) ? flattenRecursive(arr.slice(1)) : arr);
}
console.log(flattenRecursive([
[2, 7],
[8, 3],
[1, 4], 7
]))
//returns [ 2, 7, 8, 3, 1, 4, 7 ]

Here's a working version that's slightly less verbose.
//using reduce
function flattenRecursive(arr) {
return arr.reduce(function(result, a){
return result.concat(Array.isArray(a) ? flattenRecursive(a) : a);
}, []);
}
//without reduce
function flattenRecursive2(arr) {
if (arr.length === 0)
return arr;
var head = arr.shift();
if (Array.isArray(head))
return flattenRecursive2(head).concat(flattenRecursive2(arr));
else
return [head].concat(flattenRecursive2(arr));
}
var testArray = [1,[2, 3],[[4, 5, [6, 7]], [8, 9]], 10];
console.log(flattenRecursive(testArray));
console.log(flattenRecursive2(testArray));
<script src="http://gh-canon.github.io/stack-snippet-console/console.min.js"></script>

Sorry, I do not have the reputation to comment. Here are my 2 cents.
1) be careful about your initial condition. Here, it seems that if your input is Μ€arr = [[1,2]], your function returns [[1,2]], while you would like it to return [1,2].
2) in the core of the recursion, you must be sure than you recursively call your function with a smaller argument. Here, you should concat the first element of your array with the flattened rest of the array. The functionslice` may be handy for that.
3) It would be also be possible to use a reduce-like function.

Related

Symmetric Difference of unknown number of arrays

Okay so I know there are multiple answers to this question but all of them use different approaches and I'm confused af rn.
The objective is to create a function that takes two or more arrays and returns an array of the symmetric difference of the provided arrays. The individual helper function is working fine but the code throws an error when I try to run it whole.
Here is my attempt:
function sym(args) {
let totalArguments = [...args];
var helper = function (arr1, arr2) {
return arr1.filter(item => arr2.indexOf(item) === -1).concat(arr2.filter(item => arr1.indexOf(item) === -1));
}
return totalArguments.reduce(helper);}
The input of sym([1, 2, 3],[2, 3, 4]) should be [1, 4]
Your code isn't working because you're only using the first argument:
function sym(args) {
let totalArguments = [...args];
This takes the first argument, args, and makes a shallow copy of the array - which doesn't accomplish anything because you aren't mutating anywhere anyway. If you wanted to accept a variable number of arguments, use argument rest syntax, to collect all arguments in an array:
function sym(...totalArguments) {
function sym(...totalArguments) {
var helper = function(arr1, arr2) {
return arr1.filter(item => arr2.indexOf(item) === -1).concat(arr2.filter(item => arr1.indexOf(item) === -1));
}
return totalArguments.reduce(helper);
}
console.log(sym([1, 2, 3], [2, 3, 4])) // [1, 4]
console.log(sym([1, 2, 3], [2, 3, 4], [0, 1])) // [0, 4], since 1 was duplicated
(There's no need to provide an initial value to the reducer)
Another option whose logic will probably be clearer to read and understand would be to iterate over all arrays and reduce into an object that keeps track of the number of times each number has occurred. Then, take the entries of the object, and return an array of the keys whose values are 1:
function sym(...args) {
const counts = args.reduce((a, arr) => {
arr.forEach((num) => {
a[num] = (a[num] || 0) + 1;
});
return a;
}, {});
return Object.entries(counts)
.filter(([, count]) => count === 1)
.map(([key]) => key);
}
console.log(sym([1, 2, 3], [2, 3, 4])) // [1, 4]
console.log(sym([1, 2, 3], [2, 3, 4], [0, 1])) // [0, 4], since 1 was duplicated

Chop of n elements from the head of an array

I am learning Javascript at Freecodecamp. This challenge is to chop of n elements from the head of an array. My code works, except when slicing at 1.
I have been at this for a while, unfortunately I do not have my head around for loops sufficiently to find my own solution. I feel its best I know why this went wrong, rather than discover an alternative method. Many thanks in advance.
function slasher(arr, howMany) {
// it doesn't always pay to be first
var newArr = [];
for (i=0; i < arr.length; i++) {
if (arr[i] <= howMany) {
return arr.slice(howMany);
}
else {
return arr;
}
}
}
slasher(["burgers", "fries", "shake"], 1);
expected output
slasher([1, 2, 3], 2) should return [3].
slasher([1, 2, 3], 0) should return [1, 2, 3].
slasher([1, 2, 3], 9) should return [].
slasher([1, 2, 3], 4) should return [].
slasher(["burgers", "fries", "shake"], 1) should return ["fries", "shake"].
slasher([1, 2, "chicken", 3, "potatoes", "cheese", 4], 5) should return
["cheese", 4].
For the benefit of future readers, one possible approach to this problem could be like this:
function sliceFrom(sequence, n) {
let result = [];
for (let value of sequence)
if (--n < 0)
result.push(value);
if (typeof sequence === 'string')
return result.join('');
return result;
}
console.log(sliceFrom('abcdefg', 3));
console.log(sliceFrom([11,22,33,44,55], 2));
console.log(sliceFrom([11,22,33,44,55], 100));
Note that, because of using the of loop, this "slicer" works with arbitrary sequences (or rather "iterable" values), including those without .length property (e.g. Map or Set objects).
From the practical standpoint, there are built-in methods Array.slice and String.slice that do exactly that when invoked with one positive argument. However, our home-made function is better than slice when dealing with "astral" characters, e.g. emojis:
function sliceFrom(sequence, n) {
let result = [];
for (let value of sequence)
if (--n < 0)
result.push(value);
if (typeof sequence === 'string')
return result.join('');
return result;
}
faces = "πŸ˜‚πŸ˜„πŸ˜"
console.log(faces.slice(1)) // not really
console.log(sliceFrom(faces, 1)) // looks fine
Check for the length of that array:
function slasher(arr, howMany) {
return arr.slice(howMany);
}
console.log(slasher([1, 2, 3], 2)); // should return [3].
console.log(slasher([1, 2, 3], 0)); // should return [1, 2, 3].
console.log(slasher([1, 2, 3], 9)); // should return [].
console.log(slasher([1, 2, 3], 4)); // should return [].
console.log(slasher(["burgers", "fries", "shake"], 1)); // should return ["fries", "shake"].
console.log(slasher([1, 2, "chicken", 3, "potatoes", "cheese", 4], 5));// should return ["cheese", 4].
.as-console-wrapper { max-height: 100% !important; top: 0; }

Why does "[[1, 2], [3, 4]].indexOf([1, 2])" return -1?

I spent the last hour and a half trying to find a bug in my code, when I finally realized that this JavaScript code:
[[1, 2], [3, 4]].indexOf([1, 2]);
returns -1, even though something such as [1, 2, 3].indexOf(1); correctly returns 0...
Why does this happen, and how can I find the correct index of the subarray?
The indexOf takes only primitive arguments and you cannot match:
[1, 2] == [1, 2]
Which obviously gives false.
You could iterate the array and check every item of the pattern and return the index.
function getIndexOf(array, pattern) {
var index = -1;
array.some(function (a, i) {
if (a.length !== pattern.length) {
return false;
}
if (a.every(function (b, j) { return b === pattern[j]; })) {
index = i;
return true;
}
});
return index;
}
console.log(getIndexOf([[1, 2], [3, 4], [7, 8]], [7, 8]));
Another way with JSON and indexOf.
function getIndexOf(array, pattern) {
return array.map(function (a) { return JSON.stringify(a); }).indexOf(JSON.stringify(pattern));
}
console.log(getIndexOf([[1, 2], [3, 4], [7, 8]], [7, 8]));

Bonfire Seek and Destroy- Freecodecamp Challenge

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.
I've these instructions:
destroyer([1, 2, 3, 1, 2, 3], 2, 3) should return [1, 1].
destroyer([1, 2, 3, 5, 1, 2, 3], 2, 3) should return [1, 5, 1].
destroyer([3, 5, 1, 2, 2], 2, 3, 5) should return [1].
destroyer([2, 3, 2, 3], 2, 3) should return [].
destroyer(["tree", "hamburger", 53], "tree", 53) should return ["hamburger"].
I've found code:
function destroyer(arr) {
var args = Array.prototype.slice.call(arguments);
args.splice(0,1);
return arr.filter(function(element) {
return args.indexOf(element) === -1;
});
}
My questions:
Can you explain this code in English, please?
Can you give a shortcut code for above challenge? please.
I've done this challenge using filter function, though t is recommended to use also 'indexOf' in order to compare value in array by the value to filter with.
````
function destroyer(arr) {
// Remove all the values
var temp = [];
for (var i = 1; i < arguments.length; i++) {
temp.push(arguments[i]);
arr = arguments[0].filter(function(value) {
return ( value !== temp[i - 1]) ;
});
}
return arr;
}
destroyer([1, 2, 3, 1, 2, 3], 2, 3);
````
function destroyer(arr) {
var args = arr.slice.call(arguments);
args.splice(0,1);
return arr.filter(function(element) {
return args.indexOf(element) === -1;
});
}
line 1 declares the overall function used for the challenge
line 2 converts the arguments of arr into a array variable named args, although this method doesn't work when optimizing in certain javascript platforms (optimization is basically what you're trying to do with your second question)
line 3 removes zero (0) and one (1) from args
lines 4 and 5 return the final result (true/false) of filtering arr for falsy and truthy by seeing whether or not an element of arg is present in arr, returning -1 if not and the element's location in arr if true
this is actually relatively optimized; there is much more more-optimized code but oftentimes it is only viable on certain javascript platforms (mozilla javascript, etc.)

difference between two arrays with javascript filter

Problem:
Compare two arrays and return a new array with any items not found in both of the original arrays. Use Array.filter and Array.indexOf to solve this.
function diff(arr1, arr2) {
var newArr = [];
//code here
return newArr;
}
diff([1, 2, 3, 5], [1, 2, 3, 4, 5]);
I am not sure how to proceed. My solution is different from the above and uses a hard coded array. How do I make mine generic ?
function arrayNotContains(element){
var arr = [1, 2, 3, 5];
if(arr.indexOf(element) === -1){
return true;
}else{
return false;
}
}
var filtered = [1, 2, 3, 4, 5].filter(arrayNotContains);
console.log(filtered);
I got one more solution below. Is that ok ?
var arr1 = [1,2,3,5];
var arr2 = [1,2,3,4,5];
var filtered = arr2.filter(function(num) {
if (arr1.indexOf(num) === -1) return num;
});
You will want to use a closure:
function notContainedIn(arr) {
return function arrNotContains(element) {
return arr.indexOf(element) === -1;
};
}
var filtered = [1, 2, 3, 4, 5].filter(notContainedIn([1, 2, 3, 5]));
console.log(filtered); // [4]
Notice this is just a generalised version of your solution, I'm not saying that this is actually a valid solution for a symmetric diff function. For that, as it was stated in your problem, you'd need to do something like
function symmDiff(a, b) {
return a.filter(notContainedIn(b)).concat(b.filter(notContainedIn(a)));
}

Categories