Flatten a jagged multi dimensional array - javascript

I need help flattening an array like this:
[1,2,[2,3],[5,[6,1],4],7]
I want it to be something like
[1,2,2,3,5,6,1,4,7].
I have searched something like this, and found [].concat.apply, but it will only take care of the two dimensional arrays.
I also want to use an algorithm that will work for any jagged multi dimensional arrays. Please help. Thx

My recommendation would be to take a dependency on lodash and use the flattenDeep function.
_.flattenDeep([1,2,[2,3],[5,[6,1],4],7])
// [ 1, 2, 2, 3, 5, 6, 1, 4, 7 ]
If you want to write your own function, you might want to peek at the lodash implementation.
In pseudo-code, here's a recursive approach:
result = []
function flatten(array)
for each element in array
if element is array
flatten(element)
else
result.append(element)
EDIT
Here's a "by-hand" approach, though I'd definitely recommend relying on the better-tested lodash implementation.
function flatten(arr, result) {
if (result === undefined) {
result = [];
}
for (var i = 0; i < arr.length; i++) {
if (Array.isArray(arr[i])) {
flatten(arr[i], result);
} else {
result.push(arr[i]);
}
}
return result;
}
console.log(flatten([1, 2, [2, 3], [5, [6, 1], 4], 7]));
// Output:
// [ 1, 2, 2, 3, 5, 6, 1, 4, 7 ]

You can wrap the concat.apply thing in a loop to handle deeply nested arrays:
while (a.some(Array.isArray))
a = [].concat.apply([], a)
or in the ES6 syntax:
while (a.some(Array.isArray))
a = [].concat(...a);

Related

Return spreaded array in arrow function

Let's assume i have this type of array:
[ [1, 2], [3, 4] ]
What i need to do is to get nested elements on the higher layer, to make it look like:
[1, 2, 3, 4]
I am trying to reach that in functional way, so the code look's like this:
const arr = [ [1, 2], [3, 4] ]
const f = Array.from(arr, x => ...x)
But that comes up with Unexpected token ... error. So what's the way to do it right?
You can use the flat method of Array:
const inp = [ [1, 2], [3, 4] ];
console.log(inp.flat());
In your case, the spread syntax is not an operator that you can use in that way, that's why the error.
As #MarkMeyer correctly pointed out in the comments, the flat is not supported yet by Edge and Internet Explorer. In this case you could go for a solution with reduce:
const inp = [[1,2], [3,4]];
console.log(inp.reduce((acc, val) => acc.concat(...val), []));
Array.from will produce an item for every item in the array passed in. It looks at the length of the passed in iterable and iterates over the indexes starting at 0. So no matter what you do in the callback (assuming it's valid), you're going to get an array of length 2 output if you pass in a two-element array.
reduce() is probably a better option here:
let arr = [ [1, 2], [3, 4] ]
let flat = arr.reduce((arr, item) => [...arr, ...item])
console.log(flat)
You could create an iterator for the array and spread the array by using another generator for nested arrays.
function* flat() {
for (var item of this.slice()) {
if (Array.isArray(item)) {
item[Symbol.iterator] = flat;
yield* item
} else {
yield item;
}
}
}
var array = [[1, 2], [3, 4, [5, 6]]];
array[Symbol.iterator] = flat;
console.log([...array]);

Recursive Loop for Generic Tree

for days I am now trying to solve the following problem. A recursive function is suppose to iterate through a dictionary that is basically a representation of a tree.
The input looks like this:
var connections = {1:[2, 3, 4], 2:[5, 6, 7], 3:[8], 4:[9, 10]}
and the corresponding tree that it represents looks like this:
The intermediate goal is to print out the numbers in a depht first search manner, namely:
My solution for this problem is:
function dfs(connections, parent) {
console.log(parent);
if (!(parent in connections)) {
return;
}
for (i = 0; i < connections[parent].length; i++) {
dfs(connections, connections[parent][i]);
}
return;
}
However, calling the function
dfs(connections, 1)
leads to the following result:
1
2
5
6
7
That means it is not returning to the previous function and continuing the for-loop. If you have any idea whatsoever, I would be very grateful.
Cheers
Your i is implicitly global, so after it iterates through 2 (which has a length of 3), i is 4, so further tests of i < connections[parent].length fail.
You could use let to fix it (for (let i = 0), but it would probably be better to use forEach instead: array methods are less verbose, less error-prone, and more functional than for loops:
var connections = {
1: [2, 3, 4],
2: [5, 6, 7],
3: [8],
4: [9, 10]
}
function dfs(connections, parent) {
console.log(parent);
if (!(parent in connections)) {
return;
}
connections[parent].forEach(prop => dfs(connections, prop));
}
dfs(connections, 1)

How reduce() higher order function works?

Below is reduce() function
function reduce(array, combine, start) {
let current = start;
for (let element of array) {
current = combine(current, element);
}
return current;
}
Now this is the question which i am solving
Use the reduce method in combination with the concat method to “flatten” an array of arrays into a single array that has all the elements of the original arrays.
Here is the solution
let arrays = [[1, 2, 3], [4,5], [6]];
console.log(arrays.reduce((flat,current)=> flat.concat(current), []));
// → [1, 2, 3, 4, 5, 6]
Now if i try this
let arrays = [[1, 2, 3], [4, [79],5], [6]];
console.log(arrays.reduce((flat, current) => flat.concat(current), []));
I get this
[1, 2, 3, 4, [79], 5, 6]
It means that this solution can get a flatten array only up to two nested array
But how it works for this
arrays = [[1, 2, 3], [4,5], [6]];
Because in reduce() function i am using
for( let elements of array) which by the way if i use
It works like this
array = [1,4,6,[6,7],7,6,8,6];
for(element of array)
console.log(element);
// 146[6,7]7686
It does not gets the value from nested array
Then how does it for the first solution
And how to write solution which works for any number of nested array i know it will use recursion but how ?
why this function can only flatten array up to one level deep ?
let arrays = [[1, 2, 3], [4, [79],5], [6]];console.log(arrays.reduce((flat, current) => flat.concat(current), []))
Because the reduce function doesn't know if you are trying to concatenate a primitive (a number) or an array. When the reduce functions tries to concatenate two arrays, it produces a single array, but it doesn't know if every element in the array is a number or an array.
Then, as you suggested, you can use recursion:
function flatten(arrayToFlatten){
return arrayToFlatten.reduce((prev, next)=>{
if(!Array.isArray(next)){ // Base case, when you have a number
return prev.concat(next);
} else { // Recursive case, when you have an array
return prev.concat(flatten(next));
}
}, []);
}
You can do:
const arrays = [[1, 2, 3],[4, [79], 5],[6]];
const getFlatten = array => array.reduce((a, c) => a.concat(Array.isArray(c) ? getFlatten(c) : c), []);
const result = getFlatten(arrays);
console.log(result);

What is faster - merge 2 sorted arrays into a sorted array w/o duplicate values

I was trying to figure out which is the fastest way to do the following task:
Write a function that accepts two arrays as arguments - each of which is a sorted, strictly ascending array of integers, and returns a new strictly ascending array of integers which contains all values from both of the input arrays.
Eg. merging [1, 2, 3, 5, 7] and [1, 4, 6, 7, 8] should return [1, 2, 3, 4, 5, 6, 7, 8].
Since I don't have formal programming education, I fint algorithms and complexity a bit alien :) I've came with 2 solutions, but I'm not sure which one is faster.
solution 1 (it actually uses the first array instead of making a new one):
function mergeSortedArrays(a, b) {
for (let i = 0, bLen = b.length; i < bLen; i++) {
if (!a.includes(b[i])) {
a.push(b[i])
}
}
console.log(a.sort());
}
const foo = [1, 2, 3, 5, 7],
bar = [1, 4, 6, 7, 8];
mergeSortedArrays(foo, bar);
and solution 2:
function mergeSortedArrays(a, b) {
let mergedAndSorted = [];
while(a.length || b.length) {
if (typeof a[0] === 'undefined') {
mergedAndSorted.push(b[0]);
b.splice(0,1);
} else if (a[0] > b[0]) {
mergedAndSorted.push(b[0]);
b.splice(0,1);
} else if (a[0] < b[0]) {
mergedAndSorted.push(a[0]);
a.splice(0,1);
} else {
mergedAndSorted.push(a[0]);
a.splice(0,1);
b.splice(0,1);
}
}
console.log(mergedAndSorted);
}
const foo = [1, 2, 3, 5, 7],
bar = [1, 4, 6, 7, 8];
mergeSortedArrays(foo, bar);
Can someone help me with time complexity of both solutions? Also, is there another, faster solution? Should I be using reduce()?
Your first function modifying passed argument and this is bad practice.
You can do it in the following way:
function mergeSortedArrays(a, b) {
return a.concat(b.filter(el => !a.includes(el))).sort();
}
The complexity also depends on the methods that you use in your code.
1) An algorithm commonly used for sorting is Quicksort (introsort as a variation of quicksort).
It has O(n log n) complexity however the worst case may still be O(n^2) in case the input is already sorted. So your solution has O( length(b) + length(a)+length(b) log (length(a)+length(b)) ) runtime complexity.
2) According to this question on the complexity of splice() the javascript function needs O(n) steps at worst (copying all elements to the new array of size n+1). So your second solution takes length of array b multiplied by n steps needed to copy the elements during splice plus the time to push().
For a good solution that works in linear time O(n+m) refer to this Java example and port it (i.e. create an array of size length(a) + length(b) and step via the indeces as shown). Or check out the very tight and even a littler faster implementation below of the answer.

Compare values in two arrays using Iterator in Javascript?

I know how to compare values in two arrays using 2 for loops however I was looking for something a bit more sophisticated like creating an iterator to iterate through one of the arrays and passing the other array to mapmethod . Is that even possible?
I'm doing a small program for class which takes an array and x arguments and I currently have extracted the values from the arguments.
function dest(arr){
var args =[];
for(var i = 1; i < arguments.length; i++){
args.push(arguments[i]);
}
return args;
}
console.log(dest([1, 2, 3, 4], 4, 4));
Now, how could I do the iterator part to compare the values inside arr and args? Thanks for the help.
The result should be the results that match from both arr and args.
You can use the built in filter method
var arr = [2, 3, 4, 5, 6];
var args = [3, 5, 6, 7];
var result = arr.filter(function(element) {
return args.indexOf(element) > -1;
});
This will filter out all the elements out that are not present in both arrays. The result is a new array that contains only the matching values [3, 5, 6].

Categories

Resources