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);
Related
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], [...], ...], ...]
How to find the greatest number of times each element occurs in a nested array?
I am looking to find the greatest number of times each element occurs in any of the subarrays. I am NOT looking for the element that occurs the most times in the entire nested array.
Let's say my nested array is [[2, 3, 5], [3, 3, 5], [2, 2, 3, 5]].
The number 2 appears two times in one of the subarrays. The number 3 appears two times in one of the subarrays. The number 5 appears one time in one of the subarrays.
The end result I am looking for is [2, 2, 3, 3, 5].
What is the best way to do this? Below is my approach, which is not very good.
function makeNewArray(arr) {
// add the # to the numbers that appear once and add the ## to the numbers that appear twice
for (var j = 0; j < arr.length; j++) {
for (var i = 0; i < arr[j].length; i++) {
if (arr[j][i] === arr[j][i+1]) {
arr[j][i] = arr[j][i] + '#';
arr[j][i+1] = arr[j][i+1] + '#';
} else {
arr[j][i] = arr[j][i] + '#';
}
}
}
// flatten the array
arr = arr.reduce(function(a, b) { return a.concat(b); });
// remove the duplicates from the array
arr = arr.filter(function(a, b) { return arr.indexOf(a) == b; });
// remove the ## and # from the array
for (var i = 0; i < arr.length; i++) {
arr[i] = parseInt(arr[i]);
}
return arr;
}
makeNewArray([[2, 3, 5], [3, 3, 5], [2, 2, 3, 5]]);
Just based on your question and not the result you expect that I don't really get, here is a working solution that will find the highest number of occurrence.
var a = [
[2, 3, 5],
[3, 3, 5],
[2, 2, 3, 5]
];
var o = {};
var max = 0;
var highest = null;
for (var i = 0; i < a.length; i++) {
for (var j = 0; j < a[i].length; j++) {
if (!o.hasOwnProperty(a[i][j])) {
o[a[i][j]] = 1;
} else {
o[a[i][j]]++;
}
if (o[a[i][j]] > max) {
max = o[a[i][j]];
highest = a[i][j];
}
}
}
//this is the number with the highest occurence
console.log(highest);
This ES6 solution iterates the sub arrays, and creates a map of the values, then in it moves the highest values to a map of the entire array. Afterwards, we map the Map entries (no pan intended) to new arrays that are filled with the numbers according to their highest count, and flatten the result.
var data = [[2, 3, 5], [3, 3, 5], [2, 2, 3, 5]];
var result = [].concat(... // flatten the end result
[... // convert the Map to entries array
data.reduce((r, s) => { // reduce the array into a map of counts
s.reduce((map, num) => map.set(num, (map.get(num) || 0) + 1), new Map) // get a Map of the current sub array counts
.forEach((v, k) => r.set(k, Math.max(r.get(k) || 0, v))); // update the global Map if the sub array count of a number is higher
return r;
}, new Map)]
.map((s) => Array.from({ length: s[1] }, () => s[0]))); // map the entries into new sub arrays
console.log(result);
I am working on a javascript function which takes an array of integers and a target as arguments. The task is to find the first pair of integers in the array whose sum is equal to the target. I have tried this several different ways, but I keep getting a timeout error for larger input arrays. Can someone please give me some pointers on how to better optimize this code? Thanks!
var sum_pairs = function(ints, s){
var r = [];
var a = true;
var l = ints.length;
for(var j = 0; j < l; j++){
if(a){
for(var i = 0; i < j; i++){
if(ints[j] + ints[i] == s){
r[0] = ints[i];
r[1] = ints[j];
a = false;
break;
}
}
}
else{
console.log('breaking');
break;
}
}
return r[0] == null ? null : r;
}
You could use some speeding mechanisms, like
single loop,
hash table for visited values
variable a for element array[i]
very short variable names (just kidding)
Long list needs 153 ms.
var sum_pairs = function (array, s) {
var a, i,
hash = Object.create(null);
for (i = 0; i < array.length; i++) {
a = array[i];
if (hash[s - a]) {
return [s - a, a];
}
if (!hash[a]) {
hash[a] = true;
}
}
};
console.log(sum_pairs([11, 3, 7, 5], 10)); // [3, 7]
console.log(sum_pairs([4, 3, 2, 3, 4], 6)); // [4, 2]
console.log(sum_pairs([0, 0, -2, 3], 2)); // undefined
console.log(sum_pairs([10, 5, 2, 3, 7, 5], 10)); // [3, 7]
console.log(sum_pairs([1, 2, 3, 4, 1, 0], 2)); // [1, 1]
console.log(sum_pairs([1, -2, 3, 0, -6, 1], -6)); // [0, -6]
console.log(sum_pairs([0, 2, 0], 0)); // [0, 0]
console.log(sum_pairs([5, 9, 13, -3], 10)); // [13, -3]
.as-console-wrapper { max-height: 100% !important; top: 0; }
For each number that we encounter while iterating the array, we add that number's expected partner target - number into a Set. As soon as we encounter a number that is already in our set, we know that its partner has already been encountered and return this pair as the solution:
// Return the first two values of 'numbers' summing up to 'target':
function sum_pairs(numbers, target) {
let paired = new Set();
for (let number of numbers) {
if (paired.has(number)) return [target - number, number];
paired.add(target - number);
}
}
// Examples:
console.log(...sum_pairs([9, 3, 7, 5, 1], 10)); // [3, 7]
console.log(...sum_pairs([4, 3, 2, 3, 4], 6)); // [4, 2]
console.log(...sum_pairs([9, 3, 6, 4, 1], 10)); // [6, 4]
This implementation has a linear runtime complexity and is therefore faster for long input arrays, but it comes with an additional memory cost.
If you are going for raw speed, replace the for-of loop with a traditional for-loop and the let variable binding with a var declaration.
I'm building an app to test different icons. Admins upload a number of icons and input how many icons must be shown at the same time. The app then displays all possible sets of icons in sequence until all combination of icons have been shown.
Now, I need a function to generate all unique icons combinations based on two number:
the number of total icons (i)
the number of icons in each set (s)
If i = 6 and s = 3, I want the output to look as follows:
[
[1, 2, 3],
[1, 2, 4],
[1, 2, 5],
[1, 2, 6],
[1, 3, 4],
[1, 3, 5],
[1, 3, 6],
[1, 4, 5],
[1, 4, 6],
[1, 5, 6],
[2, 3, 4],
[2, 3, 5],
[2, 3, 6],
[2, 4, 5],
[2, 4, 6],
[2, 5, 6],
[3, 4, 5],
[3, 4, 6],
[3, 5, 6],
[4, 5, 6],
]
Requirements:
All sets have to be unique
A number can only occur one time in a set
I have been trying to code a recursive function, but I havent anything really anything to show. I can't get my head around it :(
Based off the idea given as an answer to this question:
Computing all n-sized permutations without repetitions and without "classic" ordering
Then use C++ std::next_permutation like algorithms which work as
follows:
Go from left and find rightmost one preceeded by zero. Put one in
place of zero and sort the rest of array.
Disclaimer: My javascript is very, very rusty so I'm sure there is a much more elegant way of implementing this.
function combine(n, k) {
var result = [];
// initialize array of values
var values = [];
for (var i = 1; i <= n; i++) {
values[i - 1] = i;
}
// initialize permutations
var perm = [];
for (var i = 0; i < n; i++) {
if (i < k) {
perm[i] = 1;
} else {
perm[i] = 0;
}
}
perm.sort();
whileloop:
while (true) {
// save subresult
var subresult = [];
for (var i = 0; i < n; i++) {
if (perm[i] == 1) {
subresult.push(values[i]);
}
}
result.push(subresult);
// get next permutation
for (var i = n - 1; i > 0; i--) {
if (perm[i - 1] == 1) {
continue;
}
if (perm[i] == 1) {
perm[i - 1] = 1;
perm[i] = 0;
perm = perm.slice(0, i).concat(perm.slice(i).sort())
continue whileloop;
}
}
// no additional permutations exist
break whileloop;
}
return result;
}
Combining n elements in to number of sets with k elements each without repetitions, how to do it.
The algorithm is relatively simple, main idea: we consequently feed first set of k elements and then try to increment each element from the end of set to populate another k-set and so on.
When we can't do that we leave the process (all possible sets are ready).
function combine(n,k) {
var result = Array();
var a = Array();
// make initial (first) k-set
for (var i=1; i<=k; i++) {
a[i-1] = i;
}
j = k-1;
while (j >= 1) {
// submit current results
result.push(a.slice());
if (a[k-1] == n) {
j = j - 1;
} else {
j = k-1;
}
if (j >= 1) {
// make next k-set based on previous one
for (var i=k; i>=j; i--) {
a[i-1] = a[j-1] + i - j + 1;
}
}
}
return result;
}
Note: JavaScript arrays have start index 0 so in code we have -1 correction for array indices (cause set of possible values from 1 to n)
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)