I tried to do a recursive function with clousure, but dosent work.
I tried to add each single number from array to finally return [1,2,3,4,5,6,7,8] but just return me [1,2,5] something weird is I put a console.log inside of else it's print me all numbers.
Could you explain me why happens this
var array = [1, 2, [3, 4], 5, [6, [7, 8]]]; // --> 1,2,3,4,5,6,7,8
function recursiveArrayPrint(array) {
let resultado = []
return function iterator() {
for (let i = 0; array.length > i; i++) {
if (Array.isArray(array[i])) {
recursiveArrayPrint(array[i])()
} else {
console.log(array[i])
resultado.push(array[i])
}
}
return resultado
}
}
const test = recursiveArrayPrint(array);
console.log('test: ',test())
the bug in your program
resultado is declared inside of recursiveArrayPrint
inside iterator you call recursiveArrayPrint, which creates a new resultado each time
only the outermost resultado is returned
function recursiveArrayPrint(array) {
let resultado = [] // #1
return function iterator() {
for (let i = 0; array.length > i; i++) {
if (Array.isArray(array[i])) {
recursiveArrayPrint(array[i])() // #2
} else {
console.log(array[i])
resultado.push(array[i])
}
}
return resultado // #3
}
}
how to fix
function recursiveArrayPrint(array) {
// no need for resultado
function* iterator() { // use generator
for (let i = 0; array.length > i; i++) {
if (Array.isArray(array[i])) {
yield *recursiveArrayPrint(array[i]) // yield
} else {
yield array[i] // yield, not console.log
// no need for resultado
}
}
// no need for resultado
}
return iterator()
}
const arr = [1, 2, [3, 4], 5, [6, [7, 8]]]
for (const x of recursiveArrayPrint(arr))
console.log(x)
1
2
3
4
5
6
7
8
we can do better
recursiveArrayPrint is not a good name. We've disentangled the console.log effect from the iteration and the caller is free to decide what to do with the output. We can just call it recursiveArray instead
Inner function iterator is a bit useless now. No need for this abstraction anymore
for loop with i is prone to off-by-one errors. We can write this in a clearer way using for..of loop
function* recursiveArray(array)
{ for (const x of array)
if (Array.isArray(x))
yield *recursiveArray(x)
else
yield x
}
const arr = [1, 2, [3, 4], 5, [6, [7, 8]]]
for (const x of recursiveArray(arr))
console.log(x)
If you are hoping to get the flattened array as a return value, JavaScript provides Array.from which turns any iterable into an array -
console.log(Array.from(recursiveArray(arr)))
[1,2,3,4,5,6,7,8]
not just for arrays
We can rename recursiveArray to traverse and it can operate as a generic function
Instead of assuming the first input is an array, we check isArray first and only then run the for..of loop. Otherwise simply yield the input
function* traverse(t)
{ if (Array.isArray(t))
for (const x of t)
yield *traverse(x)
else
yield t
}
const arr = [1, 2, [3, 4], 5, [6, [7, 8]]]
for (const x of traverse(arr))
console.log(x)
1
2
3
4
5
6
7
8
Writing robust functions will help you in the long run. traverse could easily be expanded to step through Objects now or other iterables. Now when non-array input is given, instead of an error, we get a sensible result -
for (const x of traverse(123))
console.log(x)
123
So what the recursiveArrayPrint function does, is that it creates a variable and returns a function that has closure over this variable. I think you get this part. The part that might confuse you, is that when you encounter an array, you call the recursiveArrayPrint function again, which will only create a new variable and return a new function that has closure over this variable, but this is in an entirely different scope.
It might make more sense to look at it this way.
var array = [1, 2, [3, 4], 5, [6, [7, 8]]]; // --> 1,2,3,4,5,6,7,8
function recursiveArrayPrint(array) {
let resultado = []
return function iterator() {
for (let i = 0; array.length > i; i++) {
if (Array.isArray(array[i])) {
// This is where a new closure is created, but with reference to another variable
let resultado1 = []
return function iterator1() {
for (let i = 0; array.length > i; i++) {
if (Array.isArray(array[i])) {
// This is where a new closure is created, but with reference to another variable
}
return resultado1
}
}
} else {
console.log(array[i])
resultado.push(array[i])
}
}
return resultado
}
}
const test = recursiveArrayPrint(array);
console.log('test: ',test())
I think what you intend to do is something more like this.
var array = [1, 2, [3, 4], 5, [6, [7, 8]]]; // --> 1,2,3,4,5,6,7,8
function recursiveArrayPrint() {
let resultado = []
return function iterator(array) {
for (let i = 0; array.length > i; i++) {
if (Array.isArray(array[i])) {
iterator(array[i])
} else {
console.log(array[i])
resultado.push(array[i])
}
}
return resultado
}
}
const test = recursiveArrayPrint();
console.log('test: ',test(array))
I think the main problem in your code is not just a difficulty in mastering the different elements of the JavaScript language or simply knowing how to play with the scope of variables for example.
No, it's just algorithmic, and that's arguably the hardest thing to do. We all fell into the trap of doing complicated things by trying in vain to make them simple.
It takes time to experience, to practice incessantly to accustom our brain to handling concepts in order to know how to put them together without fail.
Recursive solution:
const arr1 = [1, 2, [3, 4], 5, [6, [7, 8]]];
function recursiveflat(arr)
{
let res = []
for (let el of arr)
if (Array.isArray(el)) res.push(...recursiveflat(el) )
else res.push(el)
return res
}
console.log( JSON.stringify( recursiveflat( arr1 )))
.as-console-wrapper {max-height: 100%!important;top:0}
The idea of a closure as a recursive function seems incompatible to me, the only thing that can be done is to have a recursive function in a closure.
const arr1 = [1, 2, [3, 4], 5, [6, [7, 8]]]
, arr2 = ['a','b',[['c','d'],'e','f'],'g',['h',['i',['j'],'k'],'l'],'m']
function recursiveflatClosure(arr_in)
{
let resFLat = [] // global array is more efficient in this case
recursiveflat(arr_in) // builder
return ()=>resFLat // closure answer
function recursiveflat(arr)
{
for (let el of arr )
if (Array.isArray(el)) recursiveflat(el)
else resFLat.push(el)
}
}
let test1 = recursiveflatClosure( arr1 )
, test2 = recursiveflatClosure( arr2 )
console.log('test 1:', JSON.stringify( test1() ))
console.log('test 2:', JSON.stringify( test2() ))
.as-console-wrapper {max-height: 100%!important;top:0}
This is a somewhat strange requirement. It seems that the desired API is for a function that accepts a nested array and returns a function that returns the flattened version of it. It's unusual as generally we would not have any need for that intermediate function.
But we can update your code to get this to work. The biggest problem is that your internal recursive iterator function takes no argument. When you make a recursive call to it, there is no place to store the data you want to process. So let's make this accept an array argument:
function iterator(array) {
Now we note that this internal function is supposed to be recursive, but you're trying to call the outer function from inside. Let's call this instead:
if (Array.isArray(array[i])) {
iterator(array[i])
} else {
And now, we can't return that iterator function directly. We need to add a level of indirection, one that passes our original array into it.
function iterator(array) {
// ...
}
return function() {return iterator (array)}
Together, these make for what seems to be the smallest change to make your function work correctly:
const array = [1, 2, [3, 4], 5, [6, [7, 8]]]; // --> 1,2,3,4,5,6,7,8
function recursiveArrayPrint(array) {
let resultado = []
function iterator(array) {
for (let i = 0; array.length > i; i++) {
if (Array.isArray(array[i])) {
iterator(array[i])
} else {
console.log(array[i])
resultado.push(array[i])
}
}
return resultado
}
return function() {return iterator (array)}
}
const test = recursiveArrayPrint(array);
console.log('test: ',test())
.as-console-wrapper {max-height: 100% !important; top: 0}
However
This does not sound like a good idea. If you want a flattened version of an array, other answers and comments here suggest better ways, including better recursive solutions.
Moreover, recursion is a technique strongly associated with functional programming, and a fundamental tenet of functional programming is to avoid data mutation. But this solution is built around mutating your temporary array, resultado. I would look to the other answers for better ways of doing this.
A simple and clean flattening approach based on a reduce task and a recursively used reducer function ... no need of a closure either ...
function collectItemFlattened(list, item) {
return list.concat(
Array.isArray(item)
? item.reduce(collectItemFlattened, [])
: item
);
}
console.log(
[1, 2, [3, 4], 5, [6, [7, 8]]].reduce(collectItemFlattened, [])
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
Related
I understand the concept, but I don't understand how it is happening.
Once I get to this line:
flat = flat.concat(flatten(arr[i]))
I do not understand how that specific sub array that is about to be looped through makes it to flat = [].
I understand once the function is called again, that the sub array is basically being worked on as if it were not a sub array, and starts going though the loop.
The arr[i] makes it down to flat.push(arr[i]);
Is it at that point that the arr[i] items are being pushed into flat = [], or are they being pushed/directed here flat.concat(flatten(arr[i])) to be resolved and concat'ed when the sub array has finished going through the loop?
What confuses me is that if they are just pushed straight into flat = [] using flat.push(arr[i]), what is there to .concat?
There would be no info/data there to be able to concat on this line flat.concat(flatten(arr[i]))
What am I missing?
const arr = [[1, 2, 3], [4, [5, 6]], [[7], [8, [9]]], 10]
function flatten(arr) {
let flat = [];
for (let i = 0; i < arr.length; i++) {
if (Array.isArray(arr[i])) {
flat = flat.concat(flatten(arr[i]));
} else {
flat.push(arr[i]);
}
}
return flat;
}
It's a pretty straightforward recursive function
it takes an array or an element and return a new array with the concat of the flatten array or the element
it does the same of Arr.flat(Infinity)
you can check with the code above
const arr = [[1, 2, 3], [4, [5, 6]], [[7], [8, [9]]], 10]
function flatten(arr) {
let flat = [] ;
for (let i = 0; i < arr.length; i++) {
if (Array.isArray(arr[i])) {
flat = flat.concat(flatten(arr[i]));
} else {
flat.push(arr[i]);
}
}
return flat ;
}
console.log(flatten(arr))
console.log(arr.flat(Infinity))
So, let's consider we have such array:
let array = ["Alex", 1,2,["Marta", 3], [[5, "Melman"]], 6];
I want to output all its elements with recursive function.
My function is as follows:
function recursive(arr){
for(let f of arr){
if(typeof(f)==="object"){
return recursive(f);
}
else{
return f;
}
}
}
But it doesn't work properly. So where is the problem ?
You need to log the item to the console. And don't return from the if:
if (typeof f === "object") {
recursive(f);
}
else {
console.log(f);
}
(Also note that typeof is intended to be an operator, not a function.)
You end the function with the first return statement.
Instead, you could take a Generator and yield the items or the inner array by calling the function.
function* recursive (arr) {
for (let f of arr) {
if (Array.isArray(f)) yield* recursive(f);
else yield f;
}
}
let array = ["Alex", 1, 2, ["Marta", 3], [[5, "Melman"]], 6];
console.log(...recursive(array));
I have a function which takes an array and further arguments like this:
function arrayandmore([1, 2, 3], 2, 3)
I need to return the array ([1, 2, 3]) without those elements which equals the arguments coming behind the array. So in this case, the returned array would be:
([1]).
One of my approaches is:
function destroyer(arr) {
var args = Array.from(arguments);
var i = 0;
while (i < args.length) {
var result = args[0].filter(word => word !== args[i]);
i++;
}
console.log(result);
}
destroyer([1, 1, 3, 4], 1, 3);
Console returns:
[ 1, 1, 4 ]
I don't understand, why it returns one too - I don't understand, why it does not work.
It is the same with using splice.
function destroyer(arr) {
var args = Array.from(arguments);
var quant = args.length - 1;
for (var i = 1; i <= quant; i++) {
if (arr.indexOf(args[i]) !== -1) {
arr = arr.splice(arr.indexOf(args[i]));
}
console.log(arr);
}
}
destroyer([1, 1, 3, 4], 1, 3);
I think, both ways should work. But I don't figure out why they don't.
Your while won't work because result is being overwritten in every loop. So, it only ever removes the last parameter to the destroyer function
You can use the rest parameter syntax to separate the array and the items to be removed.
Then use filter and includes like this:
function destroyer(arr, ...toRemove) {
return arr.filter(a => !toRemove.includes(a))
}
console.log(destroyer([1, 1, 3, 4, 5], 1, 3))
I was working on the steamroller problem of free code camp. I came up with two solutions: one works perfectly [1, 2, 3, 4], but the other is giving [ 4 ].
The problem asks to write a function that flatten any sort of array.
my working code:
function steamrollArray(arr) {
newArr = [];
for (i = 0; i < arr.length; i++) {
if (Array.isArray(arr[i]) === true) {
newArr = newArr.concat(steamrollArray(arr[i]));
} else if (Array.isArray(arr[i]) === false) {
newArr.push(arr[i]);
}
}
return newArr;
}
steamrollArray([1, [2], [3, [[4]]]]);
my not working code:
function steamrollArray(arr) {
newArr = [];
for (i = 0; i < arr.length; i++) {
if (Array.isArray(arr[i]) === true) {
newArr.push(steamrollArray(arr[i]));
} else if (Array.isArray(arr[i]) === false) {
newArr.push(arr[i]);
}
}
return newArr;
}
steamrollArray([1, [2], [3, [[4]]]]);
Can anyone explain why the second code give only [4] ?
That happens because in first code, you are using
newArr = newArr.concat() in which you are assigning it to local variable. So, even in recursion, the state will be stored.
But in second code, you are using newArr.push() and in recursion, you are again declaring newArr=[];
That is the issue.
function steamrollArray(arr) {
// I'm a steamroller, baby
let newArr = arr.slice(); //newArr is a copy of arr
var result = []; //this is going to be the flatted array
checkArray(newArr, result);
return result;
}
function checkArray(myArray, resultedArray) {
//this function is a a recursion
return myArray.map(elt => {
if (!Array.isArray(elt)) {
//so this is a simple element, it is not an array, lets push it
resultedArray.push(elt);
return elt;
} else {
return checkArray(elt, resultedArray);
}
});
}
console.log(steamrollArray([1, {},
[3, [
[4]
]]
])); // should return [1, {}, 3, 4].
Comments in the code are helpful
I wanted to change the rows into columns of an array.
[
[1],
[1,2],
[1,2,3],
[4,2,3],
[4,5,3],
[4,5,6]
]
to
[
[1,1,1,4,4,4],
[2,2,2,5,5],
[3,3,3,6]
]
I tried
var res = [];
for(i in this.fields) {
for(j in this.fields[i].value) {
if(i === 0) res[j] = [];
res[j][i] = this.fields[i].value[j];
}
}
this gives me empty set.
Create this function:
function transpose(arr) {
return Object.keys(arr[0]).map(function (c) {
return arr.map(function (r) {
return r[c];
});
});
}
and then:
var transposedArray = transpose(originalArray);
What you're asking looks a little weird because you have different lengths and you're ignoring undefined values, but it is still achievable.
Don't use for..in loops for Array, use a normal for. Also, you'll need to know how many items you'll have in your new parent Array, which is the max of the lengths of the original child Arrays.
var arrR = [ // will refer to "down" and "across" as in this literal
[1],
[1, 2],
[1, 2, 3],
[4, 2, 3],
[4, 5, 3],
[4, 5, 6]
];
function r2c(arr) {
var arrC = [], // next get the longest sub-array length
x = Math.max.apply(Math, arr.map(function (e) {return e.length;})),
y = arr.length,
i, j;
for (i = 0; i < x; ++i) { // this is the loop "down"
arrC[i] = [];
for (j = 0; j < y; ++j) // and this is the loop "across"
if (i in arr[j])
arrC[i].push(arr[j][i]);
}
return arrC;
}
var arrC = r2c(arrR);
/* [
[1, 1, 1, 4, 4, 4],
[2, 2, 2, 5, 5],
[3, 3, 3, 6]
] */
You should still consider if you're happy with [[1], [1, 2], [1]] becoming [[1, 1, 1], [2]], which I would consider unexpected (the position of 2 is completely lost), but seems to be what you intend.
Similar to Pauls but doesn't need to get the max length first:
function transpose(arr) {
// Loop over arrays as long as one has values
// Arrays should be contiguous, may fail if sparse
for (var result = [], i=0, more; more; i++) {
more = false;
// Get the ith element of each array (if there is one)
for (var j=0, jLen=arr.length; j<jLen; j++) {
// Don't add missing members
if (arr[j].hasOwnProperty(i)) {
// Add array for result if not already there
result[i] = result[i] || [];
// Do transpose
result[i][j] = arr[j][i];
// Only keep going while there is data
more = true;
}
}
}
return result;
}
BTW, a fixed version of your original function is:
function transpose2(fields) {
// Make sure the result array is initialised
var res = [];
// Don't forget to keep counters local - declare them
// I've removed *this* as it's a plain function, use it if
// it's an instance method
for(var i in fields) {
// Values are read directly, there is no "value" accessor
for(var j in fields[i]) {
// Don't rely on order of enumeration - may not start at 0
if(!res[j]) res[j] = [];
// Do the transpose
res[j][i] = fields[i][j];
}
}
return res;
}
But as noted above, for..in is not liked for arrays, particularly as there are many libraries that extend built-ins like Array.prototype so you will traverse those properties too. But if you're cool with that, this is a good way to deal with sparse arrays. You can add a hasOwnProperty test to avoid inherited enumerables.
Note also that the order of enumeration isn't necessarily from '0' or in any particular order, hence changed way of initialising res[j].