Javascript Function Not Flattening Array Properly - javascript

I put together a JavaScript function that is supposed to flatten a nested array. However, this method always just returns the original array. For example, running this function with the following array [1, 2, 3, [4, 5, [6], [ ] ] ] will just return that array. I know there are ways to do this with reduce, but what logical reason is preventing this method from working? .map should allow me to manipulate a return value and return that within the new array through the recursion call.
function mapper(array) {
return array.map((item) => {
return (Array.isArray(item)) ? mapper(item) : item
}
)}

what logical reason is preventing this method from working?
var m = [1, 2, 3, [4, 5, [6], []]];
function mapper(array) {
return array.map((item) => {
// for 1,2,3 it will return item
// when it sees an array it will again call mapper & map
// function will return a new array from it, so map on
// [4, 5, [6], []] will return a new array but will not take out
// individual element and will put it in previous array
return (Array.isArray(item)) ? mapper(item) : item
}
)}
mapper(m)
map function does not mutate the original array but it will return a new array.

You are mapping the array to itself. Basically because map will return an array with exact same number of elements as the input. You cant expect it to return more, so you cant use it for flattening the array.
Should use reduce instead:
function flatten(obj) {
if (Array.isArray(obj)) {
return obj.reduce((a, b) => a.concat(flatten(b)), []);
} else {
return [obj];
}
}

Related

Iterating inside JavaScript filter method

I am trying to compare two given parameters of a function. The exact problem is as follows:
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.
Note
You have to use the arguments object.
console.log(destroyer([1, 2, 3, 1, 2, 3], 2, 3)); // expected output: [1,1]
I am using filter method to iterate over the array but I couldn't compare the args with the elements of the array inside the callback of the filter.
function destroyer(arr, ...args) {
let result = arr.filter(num => {
for (let i = 0; i<=args.length; i++ ){
num !== args[i]
}
});
return result;
}
console.log(destroyer([1, 2, 3, 1, 2, 3], 2, 3));
I can iterate with for loop but I cannot use the output of for loop to do filter iteration.
Any ideas?
Probably an easier way to achieve the goal using .filter() with .includes(). Additionally you can use ...rest so called rest parameters for you function, see form the documentation:
The rest parameter syntax allows us to represent an indefinite number of arguments as an array.
Try as the following:
const destroyer = (arr, ...rest) => {
return arr.filter(num => !rest.includes(num));
}
console.log(destroyer([1, 2, 3, 1, 2, 3], 2, 3));
I hope this helps!
The filter() method creates a new array with all elements that pass the test implemented by the provided function.
Example:
const words = ['spray', 'limit', 'elite', 'exuberant', 'destruction', 'present'];
const result = words.filter(word => word.length > 6);
console.log(result);
// expected output: Array ["exuberant", "destruction", "present"]
via MDN
Filter iterates over all elements of some array and returns a new array. It puts an element in the new array only if callback (your function invoked as a parameter of filter) return true otherwise it's omitted.
Next it's worth to use rest parameters to achieve two arrays (initial and values to exclude).
The rest parameter syntax allows us to represent an indefinite number of arguments as an array.
function sum(...theArgs) {
return theArgs.reduce((previous, current) => {
return previous + current;
});
}
console.log(sum(1, 2, 3));
// expected output: 6
console.log(sum(1, 2, 3, 4));
// expected output: 10
Solution with explanation:
//Declare your function, first parameter is initial array, the second one is also array created by using rest parameters
function destroyer(initialArray = [], ...toExclude) {
// filter initialArray, if el (single element) is NOT included in "toExclude" it returns true
// and add this particular element to the result array
let result = initialArray.filter(el => toExclude.includes(el) == false);
//return result
return result;
}

JS Array.sort. How to remove matching value from array

I am performing an Array.sort with the compare method like so:
orderNotes(notes){
function compare(a, b) {
const noteA =a.updatedAt
const noteB =b.updatedAt
let comparison = 0;
if (noteA < noteB) {
comparison = 1;
} else if (noteA > noteB) {
comparison = -1;
}
return comparison;
}
return notes.sort(compare)
}
Now since I need to sort the array anyway and loop through each element with the Array.sort, I want to use this chance to check if the note.id matches on the neighboring note, and remove the duplicate from the array (doesn't matter which one). This will save me the trouble to loop again just to check duplication.
Is it possible to alter the array inside the compare() function and remove the duplicate?
Best
Is it possible to alter the array inside the compare() function and remove the duplicate?
You could .splice(...) the element out of it if it doesn't match, but actually this:
This will save me the trouble to loop again just to check duplication.
Is a missconception. Looping an array and doing two tasks will only be slightly faster than two loops, as only the looping part gets duplicated, not the tasks done in the loop. Therefore just:
const ids = new Set;
const result = array
.filter(it => ids.has(it.id) && ids.add(it.id))
.sort((a, b) => a.updatedAt - b.updatedAt);
It might be a simpler solution to use Array.prototype.reduce to remove the duplicates in an additional step instead of while sorting:
//Starting with your ordered array
var ordered = [1, 1, 2, 3, 3, 3, 4, 5, 6, 7, 7, 8, 9, 9, 9];
//Now create a new array excluding the duplicates
var orderedAndUnique = ordered.reduce((accum, el) => {
if (accum.indexOf(el) == -1) {
accum.push(el);
return accum;
}
return accum;
}, []);
console.log(orderedAndUnique);

Can a "Rest" Operator Get Written in Javascript which Doesn't use Iteration?

In Lisps that I've seen there usually exists a 'rest' operator which returns all elements of a list in the same order, without the 0th element of that list. Without using iteration (no for or while loops), and without iteration happening via some other function or method does there exist a way to create a similar rest operator in Javascript?
For example, rest([1, 2, 3]) should return [2, 3], and rest([10, 2, 6]) should return [2, 6], and in general where 'arr' is the passed in array rest([arr[0], ..., arr[n]]) should return [arr[1], ..., arr[n]].
I had the idea of having a default value of 0 for a hidden 'counter' parameter of rest, but what I have below doesn't quite work:
function rest(arr, counter = 0)
{
if (counter === arr.length)
{
return [];
}
else
{
rest(arr, counter + 1)[0] = arr[counter]
}
}
In ES6, you can use rest syntax of Array destructuring. As from Docs:
When destructuring an array, you can unpack and assign the remaining part of it to a variable using the rest pattern:
Demo:
let rest = (arr) => {
let [first, ...rest] = arr;
return rest;
};
console.log(rest([1, 2, 3]));
console.log(rest([10, 5, 6]));

Array in JS - says it's object

I'm new to JS and I have a task to flatten an array. What I have is [1,2,[3,4]] and I have to turn it into [1,2,3,4]. I use the following approach
function flatten(arr){
return arr.reduce(function(a, b) {
return a.concat(b);
});
}
But it says concat is not a function.
Array.isArray(arr); //returns true
Can someone tell me what to do here :)
Your arr value is [1,2,[3,4]]. That's an array with three entries: the number 1, the number 2, and the array [3,4].
The first two arguments to a reduce callback are
the accumulator (which is the return value of the previous invocation of the callback) and
a value taken in sequence from the array.
You don't supply an initial accumulator in your code, so that means the first call to the reduce callback accepts the first two entries as arguments (i.e., the first element of the array is used as an accumulator). concat is not a function because the initial value of a is a number, not an array. You tested to see if arr was an array, but did not test to see if a was an array.
The clear solution here is to supply an initial value to use as an accumulator:
function flatten(arr){
return arr.reduce(function(a, b) {
return a.concat(b);
}, []);
}
This creates an empty array (with []) and then supplies it as the a value for the first call to the reduce callback.
You can use this solution:
var array = [].concat.apply([], [1,2,[3,4]]);
console.log(array);
DOCS about the array says
The JavaScript Array object is a global object that is used in the construction of arrays; which are high-level, list-like objects.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array
In your example for reduce you will have to provide an initial value which can b an empty array.
var flattened = [1,2,[3,4]].reduce(function(a, b) {
return a.concat(b);
}, []);
console.log(flattened)
You just need to provide array [] after the callback function as initial value to reduce, otherwise that value will be first element of your array. So in your code in first iteration of reduce you are trying to do this a.concat(b) where a is 1 and b is 2 and that throws the error.
function flatten(arr) {
return arr.reduce(function(a, b) {
return a.concat(b);
}, []);
}
console.log(flatten([1, 2, [3, 4]]))
concat is expecting an Array of values to join with another Array. The reason this is failing is because your Array isn't a full matrix.
The problem with your array is that it wouldn't be wise to create a single hard coded solution for that specific Array, you should probably create a recursive function like:
function flattenArr(arr) {
const newArr = []
function recursiveFlat(a) {
if(!Array.isArray(a)) {
newArr.push(a);
}
else {
for (let i = 0; i < a.length; i++) {
if(Array.isArray(a[i])) {
recursiveFlat(a[i]);
}
else {
newArr.push(a[i]);
}
}
}
}
arr.forEach(item => recursiveFlat(item));
return newArr;
}
arr = [1, 2, [3, 4]]
console.log(flattenArr(arr))
That way it will always flatten the array no matter the shape.
You just make sure that arr is an array. But,you call concat on a.
test this:
if(a instanceof Array)
You can use Array.prototype.concat() combined with Spread syntax:
var flattened = [].concat.call([], ...[1, 2, [3, 4]]);
console.log(flattened);

How would you use .reduce() on arguments instead of a specific array or object?

I want to define a function .flatten that flattens several elements into one single array. I know that the following is not possible, but essentially I would like to do this:
var flatten = function() {
var flattened = arguments.reduce(function(acc, elem) { return acc.concat(elem) }, []);
return flattened;
};
var arr = [[1,2,3], 4, 5, [6, 7]];
console.log(flatten(arr)) // => [1,2,3,4,5,6,7]
I get the following error:
TypeError: arguments.reduce is not a function
I understand that the above error is because arguments is only array-like, so it does not have the full capabilities of a true array. So there is the following, but I'm wondering if there is something even cleaner:
var flatten = function() {
var flattened = [].reduce.call(arguments, function(acc, elem) { return acc.concat(elem) });
return flattened;
};
Any good way to rewrite .flatten using .reduce()?
NOTE: I know there are many other ways that you can flatten arrays in javascript, but what I was wondering about here is how to do so with specifically arguments.
Convert the arguments object to an array first:
var flatten = function() {
var args = Array.prototype.slice.call(arguments);
var flattened = args.reduce(function(acc, elem) { return acc.concat(elem) }, []);
return flattened;
};
Or, use array methods on the arguments object directly:
var flatten = function() {
var flattened = Array.prototype.reduce.call(arguments, function(acc, elem) { return acc.concat(elem) }, []);
return flattened;
};
In ES6, you can use Array.from() to convert any iterable or array-like object to an actual array:
var flatten = function() {
var args = Array.from(arguments);
var flattened = args.reduce(function(acc, elem) { return acc.concat(elem) }, []);
return flattened;
};
Update in 2021 using modern language features:
function flatten(...args) {
return args.flat();
}
And, you can pass a depth to .flat(n) (default is 1) if you want to flatten to a deeper level than just one level down.
Though, since we have these more modern features now built into the language, you probably can use them more directly rather than passing them to a function to work on them, but we'd have to see some particular use cases to make suggestions for inline ways to solve your problem rather than using this function.
FYI, there are lots of ways to flatten an array:
Merge/flatten an array of arrays
How to flatten nested array in javascript?
Flattening multidimensional Arrays in JavaScript
Array.prototype.flat() method
Use Array.prototype.concat.apply([], arguments)
function flatten() {
return Array.prototype.concat.apply([], arguments);
}
console.log(flatten([1, 2], 3, 4, [5, 6]));
If that looks ugly, and you don't mind creating an unused empty array, you can use:
[].concat.apply([], arguments)
In ES6 though, you can get the best of both worlds:
[].concat(...arguments)
I hope my answer is helpful, I know this post was from 2016.
function flatten(...args){
const values= args.reduce(function sumNumss(total,element){
return `${total} ${element}`
});
return values
}
flatten('sdfe',[1,23,1,23],'sedfe')
using rest of (...args) syntax for the argument should do the trick.
You can also test if elements are subArraythis way :
function flatten(arr){
var res = [];
arr.forEach(x => Array.isArray(x) ? x.forEach(y => res.push(y)) : res.push(x));
return res;
}
console.log(flatten([[1,2,3], 4, 5, [6, 7]])); // [ 1, 2, 3, 4, 5, 6, 7 ]
Something like this:
BTW: I think using push is better than concat, as this doesn't create a new array
function flatten()
{
return flatten0([], Array.from(arguments));
function flatten0(res, arrToFlatten)
{
for (let el of arrToFlatten)
Array.isArray(el) ? flatten0(res, el) : res.push(el);
return res;
}
};
let arr = [1, 2, [3, 4], [[5, [6], [[7]]]]];
console.log(flatten(arr)) // [ 1, 2, 3, 4, 5, 6, 7 ]

Categories

Resources