Create n-level nested array recursively? - javascript

I want to define a function that will create a nested array that is n-levels deep with n-elements in each level-1 array.
So, for example, the code below works for 2 levels:
function genMatrix() {
const matrix = [];
let total = 0;
for(let i=0; i<2; i++) {
matrix[i] = [];
for(let j=0; j<2; j++) {
total++;
matrix[i][j] = total;
}
}
return matrix;
}
This will output the following: [ [ 1, 2 ], [ 3, 4 ] ]
I know that I can extend this same idea by simply adding more nested loops. But I want a function that will generate a similar array of any level.
Something like this:
function genMatrix(levels) {... return matrix}
I was trying to do this recursively, but I didn't get very far :(
So how could I write a recursive function that creates an array of any depth in a similar way to the example above?

Here is the n-level nested array generator:
function getMatrix(n) {
var total = 0, levels = n;
function genMatrix(n) {
var matrix = [];
for (var i = 0; i < levels; i++) {
matrix.push(n ? genMatrix(n - 1) : ++total);
}
return matrix;
}
return genMatrix(n)[0];
}
Test:
console.log(getMatrix(2));
// → [[1, 2], [3, 4]]
console.log(getMatrix(3));
// → [[[1, 2, 3], [4, 5, 6], [7, 8, 9]], [[10, 11, 12], [...], ...], ...]

Related

Remove similar duplicates from 2D array in JavaScript

I need to remove similar duplicates as well as real duplicates from 2D array in JavaScript.
let a = [
[5, 6],
[1,1],
[6,5],
[1,1],
[3,2],
[2,3]
]
function makeUnique(arr) {
var uniques = [];
var itemsFound = {};
for(var i = 0, l = arr.length; i < l; i++) {
var stringified = JSON.stringify(arr[i]);
if(itemsFound[stringified]) continue;
uniques.push(arr[i]);
itemsFound[stringified] = true;
}
return uniques;
}
a=makeUnique(a)
console.log(a);
I have got this output:
[ [ 5, 6 ], [ 1, 1 ], [ 6, 5 ], [ 3, 2 ], [ 2, 3 ] ]
Correct should be:
[ [ 5, 6 ], [ 1, 1 ], [ 2, 3 ] ]
My code removes correctly duplicates, but I need to remove similar duplicates also.
For example if I have [3,2] and [2,3] I should remove [3,2] (the one which has bigger starting index value.)
Could you help me to fix this?
Here is an example of how you can do it:
function makeUnique(arr) {
var uniques = [];
var itemsFound = {};
arr.sort((a, b) => a[0] + a[1] - (b[0] + b[1]))
for (var i = 0, l = arr.length; i < l; i++) {
if (!itemsFound[arr[i]] && !itemsFound[[arr[i][1], arr[i][1]]]) {
uniques.push(arr[i]);
itemsFound[arr[i]] = true;
itemsFound[[arr[i][1], arr[i][0]]] = true;
}
}
return uniques;
}
I hope it helps.
There are two parts
similar should be considered
among similar, one with smaller first key should stay
1. Similar should be considered
Here you can just make the key for hashmap in such a way that similar items produce same key.
One way to do that is sort the items in the tuple and then form the key, as there are two items only, first one will be min and second one will be max
let a = [
[5, 6],
[1,1],
[6,5],
[1,1],
[3,2],
[2,3]
]
function makeUnique(arr) {
var uniques = [];
var itemsFound = {};
for(var i = 0, l = arr.length; i < l; i++) {
let [a,b] = arr[i];
const hashKey = [ Math.min(a,b), Math.max(a,b)];
var stringified = JSON.stringify(hashKey);
if(itemsFound[stringified]) continue;
uniques.push(arr[i]);
itemsFound[stringified] = true;
}
return uniques;
}
let ans1=makeUnique(a)
console.log(ans1);
2. Among similar, the one with smaller first key should stay
Now you can remember in the hashmap what the value for a key was and keep updating it based on the correct candidate
let a = [
[5, 6],
[1,1],
[6,5],
[1,1],
[3,2],
[2,3]
]
function makeUniqueSmallerFirst(arr) {
var items = {};
for(var i = 0, l = arr.length; i < l; i++) {
let [a,b] = arr[i];
const hashKey = [ Math.min(a,b), Math.max(a,b)];
var stringified = JSON.stringify(hashKey);
if (stringified in items) {
let previous = items[stringified];
if (previous[0] > arr[i][0]) {
items[stringified] = arr[i];
}
} else {
items[stringified] = arr[i] // I am just storing the array because if I see a similar item next time, I can compare if that has first item smaller or not
}
}
return Object.values(items); // this doesn't guarantee output order though
// if you want order as well . you can iterate over input array once more and arrange the items in the preferred order.
}
let ans2=makeUniqueSmallerFirst(a)
console.log(ans2);
UPDATED (More simple and faster example for ES5+):
function makeUnique(arr) {
return new Set(a.map(
arr => JSON.stringify(arr.sort((a, b) => a - b)))
)
}
const m = makeUnique(a)
console.log(m) //
OLD:
This is an example of code that makes a two-dimensional array with arrays of any length unique.
let a = [
[5, 6],
[1, 1],
[6, 5],
[1, 5],
[3, 2],
[2, 3],
[6, 5, 3],
[3, 5, 6]
]
function isUnique(uniqueArray, checkedArray) {
let checked = [...checkedArray];
let unique = [...uniqueArray];
let uniqueValue = 0;
unique.forEach(value => {
if (checked.includes(value)) {
checked.splice(checked.indexOf(value), 1)
} else uniqueValue++;
})
return uniqueValue > 0;
}
function makeUnique(array2d) {
let unique = [array2d[0]]
array2d.forEach(checkedArray => {
if (unique.some(uniqueArray => {
if (checkedArray.length !== uniqueArray.length) return false;
return !isUnique(uniqueArray, checkedArray)
}
)) return 0;
else unique.push(checkedArray)
})
return unique
}
console.log(makeUnique(a)) // [ [ 5, 6 ], [ 1, 1 ], [ 1, 5 ], [ 3, 2 ], [ 6, 5, 3 ] ]
isUnique() this function checks if the numbers in both arrays are unique, and if they are, it outputs true. We use the copy through spread operator, so that when you delete a number from an array, the array from outside is not affected.
makeUnique() function makes the array unique, in the following way:
It checks if our unique two-dimensional array has at least one array that is identical to checkedArray
The first check if the arrays are of different lengths - they are unique, skip and check for uniqueness, if !isUnique gives out true, then the array is skipped by return 0

Algorithm problem with array subsequences [duplicate]

I am trying to write a function that will take an array and n as parameters,
it will return all subsets of that array with n elements, have tried a couple things, couldn't yet succeed.
thanks to whoever put it here, this functions is way too complicated and doesn't do the job, basically what I tried to do here is to pick out one element from a 4 element array to create its 3 element subsets. It doesn't even take N as parameter. it returns all 3 element subsets but also identical ones, so I have to filter them out as well, in any case I will keep trying.
function findSubsets(array) {
var answers = [];
var firstArray = array;
for (i = 0; i < array.length; i++) {
array = firstArray;
for (var k = 0; k < array.length; k++) {
if (k != i) {
var subset = array.splice(k, 1);
answers.push(array); array.splice(k, 0, subset[0]);
}
}
}
}
That not as complicated as it seems. This one is optimized because it doesn't creates useless temporary arrays during the process.
function findSubsets(array, n) {
var answers = [];
for(var i = 0 ; i < array.length ; i += n) {
answers.push(array.slice(i, i + n));
}
return answers;
}
findSubsets([1, 2, 3, 4, 5, 6, 7, 8, 9], 2) // --> [[1, 2], [3, 4], [5, 6], [7, 8], [9]]
findSubsets([1, 2, 3, 4, 5, 6, 7, 8, 9], 3) // --> [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
You can try this solution
var subsetArray = (function() {
return {
getResult: getResult
}
function getResult(array, n) {
function isBigEnough(value) {
return value.length === n;
}
var ps = [
[]
];
for (var i = 0; i < array.length; i++) {
for (var j = 0, len = ps.length; j < len; j++) {
ps.push(ps[j].concat(array[i]));
}
}
return ps.filter(isBigEnough);
}
})();
var arr = [1, 2, 3, 4,5,6,7,8,9];
console.log(subsetArray.getResult(arr,2));

Cumulatively merge arrays based on intersection without duplicates in Javascript [duplicate]

This question already has answers here:
JS - Merge arrays that share at least one common value
(3 answers)
Closed 11 months ago.
I have data that clusters like so
[[1, 2, 3], [0, 2], [2, 4], [6, 7]].
I'd like to merge arrays that share items without duplicates.
For example, the result I'm looking for from the above data would be
[[1, 2, 3, 0, 4], [6, 7]].
My approach is to loop through the arrays, and when an intersection is found, form the union of the two arrays. Here's what I've tried:
let clusters = [[1, 2, 3], [0, 2], [2, 4], [6, 7]];
let len = clusters.length;
let i, j;
for (i = 0; i < len; i++) {
for (j = i+1; j < len; j++) {
if (clusters[i].filter(x => clusters[j].includes(x))) {
clusters[i] = [...new Set([...this.clusters[i], ...this.clusters[j]])]; // this won't work
}
}
}
The result is that the clusters array is unchanged. I think this could work if I could find a way to run the union operation recursively (when the intersection condition is met).
Or maybe there's a better approach?
You can do this with Array.reduce, pushing arrays from clusters into sets in the output array or merging if a value already exists in one of the sets in the output, and then converting back from sets to arrays once complete:
let clusters = [
[1, 2, 3],
[0, 2],
[2, 4],
[6, 7]
];
let result = clusters.reduce((c, a) => {
for (i = 0; i < c.length; i++) {
if (a.some(v => c[i].has(v))) {
a.forEach(v => c[i].add(v));
return c;
}
}
c.push(new Set(a));
return c;
}, [])
.map(s => Array.from(s));
console.log(result);
This may (I didn't really tested it, take this as an idea to begin your research) work.
You don't really need recursion since you are dealing with just one nesting level. Beware of the bad performance of this if you have a large data set.
var a=[[1, 2, 3], [0, 2], [2, 4], [6, 7]];
function match(a,b) {
//if you meant _at least_ one match between two elements
for(var i=0;i<a.length;i++) if(b.indexOf(a[i])>0) return true;
return false;
}
function combine(a,b) {
var c=[];
for(var i=0;i<a.length;i++) if(c.indexOf(a[i])<0) c.push(a[i]);
for(var i=0;i<b.length;i++) if(c.indexOf(b[i])<0) c.push(b[i]);
return c;
}
while(1) {
var found=false;
for(var i=0;i<a.length;i++) {
for(var j=0;j<a.length;j++) {
if(i==j) continue;
//if a match is found, merge the two elements and start over
if(match(a[i],a[j])) {
a[i]=combine(a[i],a[j]);
a.splice(j,1);
found=true;
break;
}
}
if(found) break;
}
//repeat until no matches were found
if(!found) break;
}
console.log(a);

Array of Arrays to an Array of Unique Values Using Reduce/Map

I have an array of arrays, that needs to become 1 array of unique values.
[1, 3, 2], [5, 2, 1, 4], [2, 1]
I want to use reduce/map to solve the problem, but it doesn't seem to be working. I have solved the problem already with nested for loops like so:
function uniteUnique(arr) {
var args = Array.from(arguments);
var arr = [];
for (var i = 0; i < args.length; i++) {
for (var j = 0; j < args[i].length; j++) {
if (!arr.includes(args[i][j])) {
arr.push(args[i][j]);
}
}
}
return arr;
}
Now I tried to solve the problem here using reduce/map, but not getting the correct solution, like so:
function uniteUnique(arr) {
var args = Array.from(arguments);
return args.reduce(
(arr, a) => a.map(n => (!arr.includes(n) ? arr.push(n) : n)),
[]
);
}
console.log(uniteUnique([1, 3, 2], [5, 2, 1, 4], [2, 1]));
I also tried to solve with reduce/map, using the older syntax, like so:
function uniteUnique(arr) {
var args = Array.from(arguments);
return args.reduce(function(arr, a) {
return a.map(function(n) {
if (!arr.includes(n)) {
return arr.push(n);
} else {
return n;
}
});
});
}
My guess is that I'm not doing something right with the return statements in the callback functions. Any help would be appreciated, thanks.
The problem is that:
arr.includes(n)
arr is an array of arrays, includes wont work there. You also never pass arr down the reduce chain.
The easiest to solve would be:
[...new Set(array.reduce((a, b) => a.concat(b), []))]
That just flattens the array, builds a Set for uniqueness and spreads it into an array. Or another elegant solution usong iterators:
function* flatten(arr) {
for(const el of arr) {
if(Array.isArray(el)) {
yield* flatten(el);
} else {
yield el;
}
}
}
const result = [];
for(const el of flatten(array))
if(!result.includes(el)) result.push(el);
Instead of using array#map use array#forEach and push unique number in the accumulator.
function uniteUnique(arr) {
var args = Array.from(arguments);
return args.reduce((arr, a) => {
a.forEach(n => (!arr.includes(n) ? arr.push(n) : n));
return arr
},[]);
}
console.log(uniteUnique([1, 3, 2], [5, 2, 1, 4], [2, 1]));
Alternatively, you can array#concat all the array and then using Set get the unique value.
const arr = [[1, 3, 2], [5, 2, 1, 4], [2, 1]],
unique = [...new Set([].concat(...arr))];
console.log(unique);

JavaScript: Swapping array values by index

I'm trying to animate the sorting of a few elements with jQuery and my sorting map is defined like this:
var mapping = [
[0, 5],
[1, 4],
[2, 3],
[3, 2],
[4, 1],
[5, 0]
];
mapping[0][0] is the element's index and mapping[0][1] is the target index.
My solution was basically this:
var elements = [1, 2, 3, 4, 5, 6];
for (var i = 0; i < elements.length; i++) {
var clone1 = elements[mapping[i][0]];
var clone2 = elements[mapping[i][1]];
elements[mapping[i][0]] = clone2;
elements[mapping[i][1]] = clone1;
}
console.log(elements);
The problem is, I am working on the object I'm modifying and the operations undo themselves; instead of reversing the array, I get the same array back.
How would I swap elements like this in JavaScript? Here's a JSFiddle example of the code.
// [source, target]
var mapping = [
[0, 5],
[1, 4],
[2, 3],
[3, 2],
[4, 1],
[5, 0]
];
var elements = [1, 2, 3, 4, 5, 6];
function swapElements(elements, mapping) {
var tmp = new Array(elements.length);
for(var i = 0, l = mapping.length; i < l; i++) {
tmp[mapping[i][1]] = elements[mapping[i][0]];
}
for(var i = 0, l = elements.length; i < l; i++) {
elements[i] = tmp[i];
}
}
swapElements(elements, mapping);
console.log(elements);​
Some functional fun with accessor function
var at = function(arr) { return function(i) { return arr[i] } };
[5,4,3,2,0,1].map( at(['a','b','c','d','e','f']) )
this will returns ['f','e','d','c','a','b']
You swap elements twice from 0 to 5 and from 5 to 0 again :) Try
for (var i = 0; i < elements.length / 2; i++)
and it will be ok.
Look here
You're changing the array during a loop of it. And it's swapped twice for each pair, so the result is the same. You may need another arr to store mapped value:
var result = [];
for(var i = 0; i<mapping.length; i++){
result[mapping[i][0]] = elements[mapping[i][1]];
}
console.log(result);
Just define a temporary array:
var elements = [1, 2, 3, 4, 5, 6];
var temp = [];
for (var i = 0; i < elements.length; i++) {
temp.push(elements[mapping[i][1]]);
}
elements = temp;
Just_Mad's answer is correct if you are only trying to reverse the list. But if you want to be able to handle any mapping configuration you will need a different approach.
If you don't care about memory space you could just iterate through the mapping list and copy the element at index "mapping[i][0]" to a new array at index "mapping [i][1]". Then just return the new array. This should handle whatever mapping you put into the system (you just have to decide if and how you want to handle mappings that leave empty spaces and/or override spaces)

Categories