Javascript a var in closure always contain the same element? - javascript

See the fiddle here JSFIDDLE
var combine = function(n, k) {
function backtracking(n, k) {
if(k){ // min(k)=1, when k=0 break
for(var i=1; i<=n; ++i){
if(!used[i]){
used[i] = true;
solution[k-1] = i; // solution.length=k
// console.log(solution.length);
arguments.callee(n, k-1);
used[i] = false;
}
}
}else{
console.log(solution);
return result.push(solution); // ?
}
}
var used = [],
result = [],
solution = [];
if(k>n){
return [];
}else{
backtracking(n, k);
}
return result;
};
var b = combine(4, 2);
console.log(b);
why the result array always contain the same element>I mean it should have the same value like the array solution?
b output:
[[3, 4], [3, 4], [3, 4], [3, 4], [3, 4], [3, 4], [3, 4], [3, 4], [3, 4], [3, 4], [3, 4], [3, 4]]
I expected this:
[[2, 1], [3, 1], [4, 1], [1, 2], [3, 2], [4, 2], [1, 3], [2, 3], [4, 3], [1, 4], [2, 4], [3, 4]]
I guess it's a closure problem .but how could I do this???
I try to make the result[] as a global var.but still not work..
I see that post. but it's closure within a loop. but this is not caused by loop problem .loop actually works fine.It's a recursive problem within a closure..

When you call:
return result.push(solution);
You're still using the same Array object again, any later change to that object will be persisted on the result.
You need to make a clone of the solution object each time you push it to result:
return result.push(solution.slice(0));
Here is the new Fiddle: Fiddle.

Related

How does a recursive call work in the function?

The purpose of this algorithm is to return an array which is split into smaller two-dimensional arrays inside of it. The length of each internal array is defined by size.
How does the recursive call with .slice() method work here?
How does it make the function return an array split into smaller sub-arrays?
I can't understand this solution.
function chunkyArrayInGroups(arr, size){
if(arr.length <= size){
return [arr];
} else {
return [arr.slice(0, size)].concat(chunkyArrayInGroups(arr.slice(size), size));
}
}
When going up the callstack, chunks get sliced away from the arrays front (.slice(size)):
[0, 1, 2, 3, 4, 5] -> [2, 3, 4, 5] -> [4, 5] -> []
Now at every recursive call there exists an intermediate value holding the sliced away part (.slice(0, size)):
[0, 1] [2, 3] [4, 5]
Now the chunked away parts get concatenated together when the stack unwinds:
[0, 1] [2, 3] [[4, 5]]
[0, 1] [[2, 3], [4, 5]]
[[0, 1], [2, 3], [4, 5]]

Javascript: Group array into ascending-order

I have an array like this:
var my_array = [[6, 2], [7, 3], [9, 4], [9, 6], [3, 7]]
... and I'd like to sort the array in different groups like below:
var result = [ [6, 2], [7, 3], [9, 4] ],
[ [9, 6], [3, 7] ]
So as you can see the sort method should group all arrays their array[1] values are matching together (like an ascending row)
SO the values in the example above result[0][1] --> 2, result[1][1] --> 3, result[0][3] --> 4 are matching together.
Also the group result[1][0] --> 6 and result[1][1] --> 7 are matching together.
BTW: The my_array - array is already sorted, so that my_array[x][1] <= my_array[x+1][1].
I have no clue how to code this, so this is all what I got till now:
var my_array = [[6, 2], [7, 3], [9, 4], [9, 6], [3, 7]]
function sort_array(array) {
var group=[];
for (var i=array[0][1]; i<3; i++) {
if (array[i+1][1] == i) {
group.push(array[i+1])
}
else {
}
}
return group;
}
console.log(sort_array(my_array));
My understanding of your request;
Take a given array of two item arrays (already sorted by item 2)
group the two item arrays by the sequential item 2's (split them when you encounter a missing int).
return the result.
var my_array = [[6, 2], [7, 3], [9, 4], [9, 6], [3, 7]]
function sort_array(array) {
var results = [];
var tmp = [];
for(var i = 0; i < array.length; i++){
tmp.push(array[i]);
if(i== array.length -1 || array[i][1] != (array[i+1][1]-1)){
results.push(tmp);
tmp = [];
}
}
return results;
}
console.log(sort_array(my_array));
You could do the grouping using recursion, it basically compares the current and the next number, if the next number is consecutive it will continue to call itself, if it's not consecutive it will add a new group then call itself until there is no next value.
"use strict" // this will allow tail call recursion in modern browsers
const my_array = [[5, 0], [6, 2], [7, 3], [9, 4], [9, 6], [3, 7], [4, 10]]
function groupConsecutiveYs([curr, ...rest], acc = [[]]) {
const next = rest[0]
// add to current group
acc[acc.length - 1].push(curr)
if (typeof next === 'undefined') // we are done return the accumulator
return acc
// if next is not consecutive add another grouping
if (curr[1] !== next[1] - 1)
acc.push([])
// call recursive function again
return groupConsecutiveYs(rest, acc)
}
console.log(
groupConsecutiveYs(my_array)
)
<script src="https://codepen.io/synthet1c/pen/KyQQmL.js?concise=true"></script>

How to get the last array that includes a certain element?

I have an array of arrays and I want to check if there is a tie between the second elements and then return the first element of the last array that makes a tie.
for example this should return 4. (the first element in the last array that has a second element that makes a tie)
var optionsArray = [[1, 10], [2, 10], [3, 10], [4, 10], [6, 14]];
It is quite simple, you need to iterate over your source array, check if the given item matches the criteria, and save it to result if it does. Now if any other item does match the criteria, result's value will be overwritten with the new matching item.
var optionsArray = [[1, 10], [2, 10], [3, 10], [4, 10], [6, 14]];
var result;
optionsArray.forEach(function(item) {
if(item[1] == 10) {
result = item;
}
});
console.log(result);
You can create a simple find function that iterates the array backwards, and returns as soon as a condition callback returns true.
var optionsArray = [[1, 10], [2, 10], [3, 10], [4, 10], [6, 14]];
function find10(s) {
return s[1] === 10;
}
function findFromTheEnd(arr, cb) {
var l = arr.length;
while(l--) { // iterate backwards
if(cb(arr[l])){ // if the callback returns true
return arr[l]; // return the item
}
}
return null; // return null if none found
}
var result = findFromTheEnd(optionsArray, find10);
console.log(result);
You can use reduceRight() and return array.
var arr = [[1, 10], [2, 10], [3, 10], [4, 10], [6, 14]];
var result = arr.reduceRight(function(r, e) {
if(e[1] == 10 && !r) r = e;
return r;
}, 0)
console.log(result)
You can also use for loop that starts from end and break on first match.
var arr = [[1, 10], [2, 10], [3, 10], [4, 10], [6, 14]];
var result;
for (var i = arr.length - 1; i >= 0; i--) {
if (arr[i][1] == 10) {
result = arr[i]
break;
}
}
console.log(result)
A classic for in the reserve order with a break seems enough :
var optionsArray = [[1, 10], [2, 10], [3, 10], [4, 10], [6, 14]];
var elementFound;
for (var i = optionsArray.length-1; i >=0; i--) {
if(optionsArray[i].item[1] == 10) {
elementFound = optionsArray[i].item[1];
break;
}
}
If elementFound is not undefined, it refers to the found array.
Rather than considering this as a multidimensional array problem, think of it as an array includes problem nested in an array search problem;
const aarr = [1, 2, 3, 4];
aarr.includes(3); // true
aarr.includes(10); // false
// and
const barr = ['hello', 'world'];
barr.find(item => item[0] === 'h'); // "hello"
barr.find(item => item[3] === 'l'); // "hello"
barr.find(item => item[1] === 'z'); // undefined
So to nest these,
const carr = [[1, 2, 3, 4], [4, 5, 6, 7]];
carr.find(arr => arr.includes(4)); // [1, 2, 3, 4]
carr.find(arr => arr.includes(6)); // [4, 5, 6, 7]
Next, we've reduced the whole problem down to "how to do this in reverse?"
You've a few options depending on how you want to implement it, but a simple way to do it is a shallow clone arr.slice() followed by a reverse arr.reverse() (we use the clone so there are no side-effects of reverse on the original array)
carr.slice().reverse().find(arr => arr.includes(4)); // [4, 5, 6, 7]
If you're working with an index, remember that you'll need to transform those too; -1 is fixed, otherwise transformed_index = arr.length - original_index - 1
Here is how you might implement the reverse of some of the Array methods
const optionsArray = [[1, 10], [2, 10], [3, 10], [4, 10], [6, 14]];
// index 0 1 2 3 4
function arrLast(arr, comparator, method = 'find', transform = x => x) {
return transform(arr.slice().reverse()[method](comparator), arr);
}
const findLast = (arr, comparator) => arrLast(arr, comparator);
const findLastIndex = (arr, comparator) => arrLast(arr, comparator, 'findIndex', (i, arr) => i === -1 ? -1 : arr.length - i - 1);
arrLast(optionsArray, arr => arr.includes(10)); // [4, 10]
findLastIndex(optionsArray, arr => arr.includes(10)); // 3
If you have to make comparisons among array items and you need to cut short once you are satisfied a while loop is ideal. Accordingly you may do as follows;
var arr = [[1, 10], [2, 10], [3, 10], [4, 10], [6, 14]],
i = 0,
result;
while (arr[i][1] === arr[++i][1]);
result = arr[i-1][0]
console.log(result);

Array element not splicing into separate array - FreeCodeCamp > Chunky Monkey

I'm working through the FreeCodeCamp challenges and I'm stuck on the following one:
Our goal for this Algorithm is to split arr (first argument) into
smaller chunks of arrays with the length provided by size (second
argument). There are several green checks (objectives) our code needs
to pass in order to complete this Algorithm:
(['a', 'b', 'c', 'd'], 2) is expected to return [['a', 'b'], ['c', 'd']]
([0, 1, 2, 3, 4, 5], 3) is expected to return [[0, 1, 2], [3, 4, 5]]
([0, 1, 2, 3, 4, 5], 2) is expected to return [[0, 1], [2, 3], [4, 5]]
([0, 1, 2, 3, 4, 5], 4) is expected to return [[0, 1, 2, 3], [4, 5]]
([0, 1, 2, 3, 4, 5, 6, 7, 8], 2) is expected to return [[0, 1], [2, 3],
[4, 5], [6, 7], [8]].
Here is the code I've come up with:
function chunkArrayInGroups(arr, size) {
var newArray = [];
var holdArray = [];
for (var i = 0; i <= arr.length; i += size) {
holdArray = arr.slice(0, size);
removed = arr.splice(0, size);
newArray.push(holdArray);
}
if (arr.length !== 0) {
newArray.push(arr);
return newArray;
}
else return newArray;
}
chunkArrayInGroups([0, 1, 2, 3, 4, 5, 6, 7, 8], 2);
The algorithm works for all of the objectives, except for the last one. Instead of returning:
[[0, 1], [2, 3], [4, 5], [6, 7], [8]]
it's returning:
[[0, 1], [2, 3], [4, 5], [6, 7, 8]] - and I can't work out why.
Any help shedding some light on where I'm going wrong would be much appreciated!
I don't blame you for being confused, it took me a while to find the problem, too.
It started with identifying that your for loop was a bit odd - you weren't using the i variable for anything. Additionally, you were splice()-ing the original array down each iteration, so your intention was correct - to wait until there is less than size elements left in the array.
So I expressed that as a while loop instead, and voila, it works:
function chunkArrayInGroups(arr, size) {
var newArray = [];
var holdArray = [];
while (arr.length >= size) {
holdArray = arr.slice(0, size);
removed = arr.splice(0, size);
newArray.push(holdArray);
}
if (arr.length !== 0) {
newArray.push(arr);
return newArray;
}
else return newArray;
}
chunkArrayInGroups([0, 1, 2, 3, 4, 5, 6, 7, 8], 2);
You are recaluculating length again and again, but you forgot that you are actually removing elements from arr, so length would not be the same as size increases length decreases,either you can give as while loop or keep original length in a variable and check
function chunkArrayInGroups(arr, size) {
var newArray = [];
var holdArray = [];
var length=arr.length;
for (var i = 0; i <=length; i += size) {
holdArray = arr.slice(0, size);
removed = arr.splice(0, size);
newArray.push(holdArray);
}
if (arr.length !== 0) {
newArray.push(arr);
return newArray;
}
else return newArray;
}
chunkArrayInGroups([0, 1, 2, 3, 4, 5, 6, 7, 8], 2);

Swapping array elements in Javascript

I have an array like this - (i do not know the length of this array in advance)
data: [[3, 1], [1, 1], [5, 1], [6, 1], [25, 1], [8, 2], [2, 3]]
how i can swap it so it becomes like this:
data: [[1, 3], [1, 1], [1, 5], [1, 6], [1, 25], [2, 8], [3, 2]]
Thanks!
Each element is an array, so you can apply reverse on it.
for(i = 0; i < data.length; i++) data[i].reverse();
var i, temp;
for(i = 0; i < data.length; i++) {
temp = data[i][0];
data[i][0] = data[i][1];
data[i][1] = temp;
}
var data = [[3, 1], [1, 1], [5, 1], [6, 1], [25, 1], [8, 2], [2, 3]];
data = data.map(function(x){ // loop though all elements
return x.reverse(); // reverse each array
});
Arary.map should work on all modern browsers (by that I mean you need Chrome, Firefox, Safari, Opera, or IE9+).
EDIT: Since Array.reverse will mutate the array, you can also do this:
var data = [[3, 1], [1, 1], [5, 1], [6, 1], [25, 1], [8, 2], [2, 3]];
data.forEach(function(x){ // loop though all elements
x.reverse(); // reverse each array
});
Array.forEach works in the same "moden" browsers, that .map works in.

Categories

Resources