Concept about flatten use recursion with reduce and concat - javascript

Now I would like to flattening the array with multiple layers
From the other solution before and the developer's network, There is an effective solution:
var arr1 = [1,2,3,[1,2,3,4, [2,3,4]]];
function flattenDeep(arr1) {
return arr1.reduce((acc, val) => Array.isArray(val) ? acc.concat(flattenDeep(val)) : acc.concat(val), []);
}
flattenDeep(arr1);// [1, 2, 3, 1, 2, 3, 4, 2, 3, 4]
However, there is a couple questions about the theory at that back that I want to ask:
1. In my understanding, the function starts with initial value acc, and then it keeps looping all the element to see if it is an array or not.
In this case, the first 3 elements are 1,2,3 -- therefore is not an array and will be accumulated and returned as an array.
But start from the fourth element it returns
Array.isArray(arr1[3])
(5) [1, 2, 3, 4, Array(3)]
In this case, it triggers the condition acc.concat(flattenDeep(val)), which is a recursion of the function. How this condition helps to flatten the array?
And for the [] at the back, if my understand is right, it indicates the return form of the result from reduce should be an array?
Last but not least, any good reference for this topics that I should look for?
May thanks for your help!
Further elaboration:
let's say, now the val in the reduce function above is [1, 2, 3, 4,[[2,3,4]]].
In this first round of checking, the elements 1,2,3,4 are not array, but the last element still does, and it makes the whole argument still an array. For the next round of the recursion, would it just take the element [[2,3,4]]] and further evaluate the elements inside this array?
Or with this example, what would be the first, second and third round outcome of the recursion process?

Second argument in the reduce method is the starting condition. If there is nothing provided, first acc will be the first array's element (see array.prototype.reduce on mdn).
I'm not sure about the But there is nth to indicate the reduction of the array statement. You're calling function recursively until you will find something that's not an array and then you're returning that value.
Look for js call stack, understand how js is stacking the functions, you can even draw it, it will help definitely because it's not easy case :)

Related

How to chain two splice methods in Javascript?

In the following example, splice().splice() didn't work. What am I missing? How can I chain two splice methods in a single line? Thank you for any advice!
function test() {
var data = [0,1,2,3,4,5];
data.splice(0,2).splice(-1); // Removed only the first two elements, but not the last element.
console.log(data) // Returned [ 2, 3, 4, 5 ]
var data = [0,1,2,3,4,5];
data.splice(0,2); // Removed the first two elements.
data.splice(-1); // Removed the last element.
console.log(data) // Returned [ 2, 3, 4 ]
}
splice returns the removed elements, so chaining them in a single statement won't really work - you don't want to operate on the removed elements, you want to operate on the original array both times.
Usually, in this sort of situation, the right approach is to use slice instead, which is both easier to work with (just specify a start (inclusive) and end (exclusive) index) and is more functional (you get a new array instead of mutating an existing one - it's nice to avoid mutation when possible, makes code more understandable).
const data = [0,1,2,3,4,5];
console.log(data.slice(2,5));
You can do this, although I don't recommend this because it makes the code confusing
const data = [0,1,2,3,4,5];
console.log(data.splice(2,4).splice(0,3)); // Returned [2,3,4]

How to use reduce to iterate an array

I have been doing research on the benefits of using reduce over map. Mainly the benefit is that reduce has better performance time.
My question is how would i be able to use reduce to iterate over an array as if i would for the map function ?
An example
The following code combines the fruits to one string. How would i be able to iterate over this array using the reduce function ?
const fruits = ['pears', 'plums', 'grapes', 'apples']
console.log(fruits.reduce( (acc, fruit) => acc.concat(fruit)))
// "pearsplumsgrapesapples"
You'll sacrifice code legibility for a millisecond of difference but here it is:
['pears', 'plums', 'grapes', 'apples'].reduce(function (acc, currentFruit) {
// Your code here
acc.push(currentFruit);
return acc;
}, []);
You'll have to push your current value to a an array because the reduce method objective is to reduce the provided data to a single value.
At the end of the day, map is just better, simpler and you won't be able to notice the time difference.
The reduce function indeed iterates over the array, because it's meant to reduce all the array items to a single value by applying a function.
The map function iterates on the array too, in order to project the array elements to a value calculated by applying a provided function.
The difference between the two functions is that they are iterating over the array for a different purpose. Reduce is used to compute single value, map is used to project each array item to a new value.
In both cases you can't say anything about the computational complexity because you are passing a callback and you don't know what that callback is going to do. So the computational complexity depends on the callback function.
Take a look at this paper about reduce: https://hackernoon.com/javascript-array-reduce-50403c421968
You could pass an empty array as the reduce function accumulators initial value, then within the body of your reduce function you can append to the acc which will now be an array and execute conditional logic.
map is simple. It calls the function on each element of the input array, and returns a new array containing the corresponding function results.
result = array.map(f)
is equivalent to
result = [f(array[0], 0, array), f(array[1], 1, array), f(array[2], 2, array), ...]
You use reduce if you want to combine the results of each iteration in a custom way, to produce one result. The function takes two arguments -- the first is the return value of the previous iteration, and the second is the current element from the iteration.
result = array.reduce(f, init)
is equivalent to
temp1 = f(init, array[0], 0, array);
temp2 = f(temp1, array[1], 1, array);
temp3 = f(temp2, array[2], 2, array);
...
result = f(tempN, array[N], N, array);
If you don't provide an initial value argument, the first line becomes
temp1 = array[0];
and the rest is the same.
If you don't actually need a new result, you just want to execute some code on each array element, you use forEach(). It's like map, but it doesn't collect the results. If you want to log each array element on a new line, you would do:
array.forEach(el => console.log(el));

Trying to follow a numerical array sort steps in Javascript

I'm learning to do an array sort in Javascript on an array of numbers, I've looked at the mdn page and done a search, this is the sort I'm trying to understand:
var numbers = [4, 2, 5, 1, 3];
numbers.sort(function(a, b) {
return a - b;
});
console.log(numbers);
// [1, 2, 3, 4, 5]
I understand what's happening, I just can't seem to find an easy to follow step by step article on the javascript array sort which shows how 'a' and 'b' are being compared and moved around, for instance once the end of the array is reached does the sort repeat itself until all items are sorted? I guess I'm curious about the implementation in a simple to understand way.
To add, I've tried console logging the output but still was a bit confused by the way it was done so looking for a more concrete answer from someone who knows.
The specific sorting algorithm used is not specified by the specification; a JavaScript engine is free to use any sorting algorithm it likes, whether that's a bubble sort, a quicksort, or something else. But yes, sorting algorithms in general need to do more than just a single pass through the data and one comparison per element. Much more.
Implementation of sort function is browser related
For example WebKit implementation: https://gist.github.com/964673
The Array.sort method is built-in. It utilizes a machine-language quicksort, which is going to be faster than anything you could do in your code. Computerphile has a good video on quicksort.
However, any sort algorithm has to know which of two elements come first, and that's what the function is for. It will be passed whatever two array elements are being compared at that point in the overall algorithm, and return:
a negative number: the first argument should sort first.
a positive number: the second argument should sort first.
zero: they are equal.
Hope this clarifies!
If you want to see what is happening, put some logging inside the comparison function:
var numbers = [4, 2, 5, 1, 3];
numbers.sort(function(a, b) {
console.log("["+numbers.join()+"], comparing element #"+numbers.indexOf(a)+" ("+a+") and #"+numbers.indexOf(b)+" ("+b+"): "+(a-b));
return a - b;
});
console.log(numbers.join());
Then with a bit of patience you can track how the elements are moving, and recognize the sorting algorithm in action.

JS Splice Array of Nulls Removes Last Index Regardless of Parameters

For a reason specific to this application an array of data or nulls is used to display a list of forms. The difference is based on whether data was provided by a service or manually added, where null indicates everything was manually added via
a button not a service.
So ignoring the use case of an array of nulls it turns out that [null, null, null].splice(0, 1); removes the null at index 2 instead of 0 based on entering values into the different forms displayed based on the array length, and then seeing who disappears on delete.
It can be made to work by adding something unique like { index: theIndex } to the array instead of null. So splice now works correctly removing the item at index 0 instead of 2, but now I'm curious what splice does under the covers that it can't remove an index regardless of its value compared to the other indices.
Can anyone explain what splice is doing? Based on the spec for splice I don't really see why this happens.
(This follows from the comments in the question but is too large to be a comment).
So I think you are having a conceptual misconception (^^). Look at this examples:
let a = [1, 2, 3]
a.splice(0, 1) // => 1
a // => [2, 3]
let b = [1, 2, 3]
delete b[0] // => true
b // => [<1 empty slot>, 2, 3]
The splice function modifies the array in-place. Note that, although we spliced the first element, we got as a result an array of two elements.
Look now at this example
let a = [1, 1, 1]
a.splice(0, 1)
a // => [1, 1]
let b = [1, 1, 1]
b.splice(2, 1)
b // => [1, 1]
We are deleting the first element from a and the last from b, but of course there's no way of telling so just looking at the result.
In the case with the nulls, the same thing is happening. Some library (Angular) is trying to figure out which element you deleted, but there's no way of knowing. This is because null === null.
Now if you use an array of empty objects, for example, there would be a way of knowing. Since {} !== {}---because each time you cast a {} you are creating a unique object---, then you could know which element is missing in an array of empty objects. The case is similar with the array [1, 2, 3].
let a = [{}, {}, {}] // lets imagine that each object has a unique id
// and we have [{}#1, {}#2, {}#3]
let [obj1, obj2, obj3] = a
obj1 === obj2 // => false, because they have different ids.
a.splice(0, 1)
a // => [{}#2, {}#3]
a.includes(obj1) // => false, because {}#1 is no longer in a
So an alternative to using an array of nulls, would be to use an array of empty objects. I think that is why the code works for you when you use objects like { index: theIndex }. But of course all depends on how smart Angular is. I bet there is a more native way of deleting an element, as #KaiserKatze points out, "it's always a bad idea to directly remove or add elements in the array if it maps to your model."
You have to understand that when you are splicing the array you're only doing that--removing an element from an array. You're not removing the "form element" when splicing the array. Instead, some foreign code is reading the array and trying to figure out --under the hood-- what you intended to do.

Why does this now work as one return statement?

I am just curious, why does this block of code work...
function slasher(arr, howMany) {
arr.splice(0, howMany);
return arr;
}
slasher([1, 2, 3], 2);
But this does not? My desired outcome is "[3]"
function slasher(arr, howMany) {
return arr.splice(0, howMany);
}
slasher([1, 2, 3], 2);
Answering such questions is simply a matter of consulting a good documentation. I recommend Mozilla's MDN - Array.splice():
The splice() method changes the contents of an array by removing
existing elements and/or adding new elements.
Return value
An array containing the deleted elements. If only one element is
removed, an array of one element is returned. If no elements are
removed, an empty array is returned.
It tells us that this method changes the array in-place, i.e. myArray.splice() directly mutates / changes myArray instead of building a new array and returning that. The returned value is actually the list of removed elements.
function slasher(arr, howMany) {
arr.splice(0, howMany);
return arr;
}
slasher([1, 2, 3], 2);
This will first 'splice' the given array, which in essence will just remove the first howMany elements from the array (2). You then return the array (which is now different as it has had it's first 2 elements removed), hence [3].
function slasher(arr, howMany) {
return arr.splice(0, howMany);
}
slasher([1, 2, 3], 2);
This will return the result of the original splice. From the documentation where it says Return value (https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/splice?v=control), this will return the elements that were removed from the array, hence [1, 2].
#Sam, splice method changes the contents of an array (itself) by removing existing elements and/or adding new elements. Hence in the first code sample, you are getting [3].
However, it returns an array containing the deleted elements. If only one element is removed, an array of one element is returned. If no elements are removed, an empty array is returned.
As a result in the second code sample, you get: [1,2].
This is because Splice returns an array containing the deleted elements, and not the remaining parts in the array. By returning arr, you returned the array after it was "slashed".
Source: Splice at MDN
Most functions in any Language are either Accessors, or Mutators. They either access a property or they change a property or structure. In this case, splice() is both. It's important to read documentation on all functions you use especially in JS as they sometimes will do more than one thing.
sometimes a function might be overloaded where one version is an accessor and the other is a mutator.
Any time you pass an array to a function make sure you know what might happen to the contents of the array.
Splice returns the removed elements while simultaneously changing the contents of the array. The return value is not the array you passed in. This is common across many languages to return a removed item from a data structure.

Categories

Resources