javascript: split multidimension array upon duplicates - javascript

I am stuck with splitting an array in multiple parts, based on pairs and duplicates.
I have this array :
var array = [[24, 17],[45, 17],[17, 24],[38, 31],[31, 38],[17, 45]];
and i need to split it to obtain this :
var array = [[24,17,45],[38,31]];
Does someone have any idea of the right way to do it ?
Any help would be greatly appreciated !

You could take the power of Set and check if the one of the value is already in one set. If not create a new result set with a new set.
var array = [[24, 17], [45, 17], [17, 24], [38, 31], [31, 38], [17, 45]],
result = array
.reduce((r, a) => {
var s = r.find(s => a.some(Set.prototype.has, s));
if (s) {
a.forEach(v => s.add(v));
} else {
r.push(new Set(a));
}
return r;
}, [])
.map(s => Array.from(s));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

You could use a hashtable that maps a certain mesh to a collision, so you can group them easily:
const array = [[24, 17],[45, 17],[17, 24],[38, 31],[31, 38],[17, 45]];
const result = [], hash = {};
for(const [a, b] of array) {
let group = hash[a] || hash[b] || (arr => (result.push(arr), hash[a] = hash[b] = arr))([]);
if(!group.includes(a)) group.push(a);
if(!group.includes(b)) group.push(b);
}
console.log(result);

You can flatten the array by spreading into Array.concat(), and then reduce the array. Using a helper Set, add a new sub array when a duplicate is detected. Afterwards, filter out the empty arrays:
const array = [[24, 17],[45, 17],[17, 24],[38, 31],[31, 38],[17, 45]];
const helperSet = new Set();
const result = [].concat(...array)
.reduce((r, n) => {
!r[r.length - 1] && r.push([]);
if(!helperSet.has(n)) {
r[r.length - 1].push(n)
helperSet.add(n);
} else {
r.push([]);
};
return r;
}, [])
.filter(({ length }) => length);
console.log(result);

Related

Find indices of all duplicate records in js

Suppose I have an array as below:
Arr1 = [12,30,30,60,11,12,30]
I need to find index of elements which are repeated in array e.g.
ans: 0,1,2,5,6
I've tried this code but it is considering just single element to check duplicates.
First get all the duplicates using filter() and then using reduce() get he indexes of only those elements of array which are in dups
const arr = [12,30,30,60,11,12,30];
const dups = arr.filter(x => arr.indexOf(x) !== arr.lastIndexOf(x));
const res = arr.reduce((ac, a, i) => {
if(dups.includes(a)){
ac.push(i)
}
return ac;
}, []);
console.log(res)
The time complexity of above algorithm is O(n^2). If you want O(n) you can use below way
const arr = [12,30,30,60,11,12,30];
const dups = arr.reduce((ac, a) => (ac[a] = (ac[a] || 0) + 1, ac), {})
const res = arr.reduce((ac, a, i) => {
if(dups[a] !== 1){
ac.push(i)
}
return ac;
}, []);
console.log(res)
You could use simple indexOf and the loop to get the duplicate indexes.
let arr = [12,30,30,60,11,12,30]
let duplicate = new Set();
for(let i = 0; i < arr.length; i++){
let index = arr.indexOf(arr[i], i + 1);
if(index != -1) {
duplicate.add(i);
duplicate.add(index);
}
}
console.log(Array.from(duplicate).sort().toString());
A slightly different approach with an object as closure for seen items which holds an array of index and the first array, in which later comes the index and a necessary flattening of the values.
This answer is based on the question how is it possible to insert a value into an already mapped value.
This is only possible by using an object reference which is saved at the moment where a value appears and which is not seen before.
Example of unfinished result
[
[0],
[1],
2,
[],
[],
5,
6
]
The final Array#flat removes the covering array and shows only the index, or nothing, if the array remains empty.
[0, 1, 2, 5, 6]
var array = [12, 30, 30, 60, 11, 12, 30],
indices = array
.map((o => (v, i) => {
if (o[v]) { // if is duplicate
o[v][1][0] = o[v][0]; // take the first index as well
return i; // return index
}
o[v] = [i, []]; // save index
return o[v][1]; // return empty array
})({}))
.flat() // remove [] and move values out of array
console.log(indices);
You could use Array#reduce method
loop the array with reduce.At the time find the index of argument
And check the arguments exist more than one in the array using Array#filter
Finaly push the index value to new accumulator array.If the index value already exist in accumalator.Then pass the currentIndex curInd of the array to accumulator
const arr = [12, 30, 30, 60, 11, 12, 30];
let res = arr.reduce((acc, b, curInd) => {
let ind = arr.indexOf(b);
if (arr.filter(k => k == b).length > 1) {
if (acc.indexOf(ind) > -1) {
acc.push(curInd)
} else {
acc.push(ind);
}
}
return acc;
}, []);
console.log(res)
Below code will be easiest way to find indexes of duplicate elements
var dupIndex = [];
$.each(Arr1, function(index, value){
if(Arr1.filter(a => a == value).length > 1){ dupIndex.push(index); }
});
This should work for you

filtering a 2D array in javascript, i tried everything

I have 2D array like this :
[[ONE,1],[QUARTER,0.25],[QUARTER,0.25]]
I want it to become :
[[ONE,1],[QUARTER,0.5]]
I tried using array.IndexOf but no luck
ONE,1,QUARTER,0.25,QUARTER,0.25
Contrary to your question title, your example suggests you want sum, as opposed to filter, the array.
let arr = [['ONE', 1], ['QUARTER', 0.25], ['QUARTER', 0.25]];
let sums = arr.reduce((sums, item) => {
let found = sums.find(([key]) => key === item[0]);
if (found)
found[1] += item[1];
else
sums.push(item);
return sums;
}, []);
console.log(sums);
You could reduce the array by looking if the same key exists.
var array = [['ONE', 1], ['QUARTER', 0.25], ['QUARTER', 0.25]],
result = array.reduce((r, [key, value]) => {
var temp = r.find(a => a[0] === key);
if (temp) {
temp[1] += value;
} else {
r.push([key, value]);
}
return r;
}, []);
console.log(result);

Check if a JavaScript 2D array is in ascending order?

Suppose I have an array as such
var arr = [ [1,2,3],[4,5,1]]
How do I check if the columns are in ascending order?
For first column 1<4 returns true but the last column returns false as 3 is not less than 1.
The result should return an array of the columns that returns false
I would do it by first transposing the matrix (simply because its easier to deal with in js) and then map over each column (converting them from a set of numbers into a bool using Array#every).
const arr = [
[1,2,3],
[4,5,1]
];
const transpose = (arr) => Array(arr[0].length)
.fill(0)
.map((_, colIndex) => {
return Array(arr.length)
.fill(0)
.map((_, rowIndex) => {
return arr[rowIndex][colIndex];
});
});
const arr2 = transpose(arr);
const arr3 = arr2.map(col => {
let previousValue = -Infinity;
return col.every(v => {
const tmp = previousValue < v;
previousValue = v;
return tmp;
});
});
console.log(arr);
console.log(arr2);
console.log(arr3);
One possible solution is to use Array.map() over the first inner array, and then check if you found some element on a particular column that is not in order using Array.some():
var arr = [
[1, 2, 3],
[4, 5, 1],
[6, 7, 2]
];
let res = arr[0].map((n, cIdx) =>
{
return !arr.some((inner, rIdx) => rIdx > 0 && inner[cIdx] < arr[rIdx - 1][cIdx]);
});
console.log(res);
.as-console {background-color:black !important; color:lime;}
.as-console-wrapper {max-height:100% !important; top:0;}
To find the "columns" with the two values in descending order, you can use parallel arrays. Here is a succinct version of the code for that. (The second snippet is more verbose and provides further explanation.)
var arr = [ [1,2,3], [4,5,1] ], result = [];
arr[0].forEach((n, i) => { // n is the number, i is the index
if(arr[0][i] > arr[1][i]){ result.push(i); } // if descending, remember this index
});
console.log(result); // Logs `[2]` (an array with the index of the third column)
If you want to change which "column" the values are in so that they will be in ascending order, you could do it like this. When you find a "misplaced" value, store it in a temporary variable to make room. Then you can assign the two values to their "correct" locations.
const bigArr = [ [1,2,3], [4,5,1] ],
a1 = bigArr[0],
a2 = bigArr[1],
resultArr = [],
len = Math.min(a1.length, a2.length); // Uses min in case they have different lengths
for(let i = 0; i < len; i++){
if(a1[i] > a2[i]){ // Checks each position, and exchanges values if necessary
exchangeValuesInParallelArrays(a1, a2, i);
resultArr.push(i); // Adds this index to the results array
}
}
console.log(bigArr); // Logs `[ [1,2,1], [4,5,3] ]`
console.log(resultArr) // Logs `[2]`
function exchangeValuesInParallelArrays(arr1, arr2, index){
let tempStorage = arr1[index];
arr1[index] = arr2[index];
arr2[index] = tempStorage;
}

How to create a key value array from two arrays

I have two arrays with the same number of elements which maps 1:1
const array1 = [1, 3, 2]
const array2 = [U2, U1, U3]
How can I generate a new array (or map) from array1 & array2 and have something like this ?
const result = [[1, U1], [2, U2], [3, U3]]
You can also use Array.forEach(). Before that you can sort the array1 array and then get the corresponding value from array2 prefixed with U to get the desired output:
const array1 = [1, 3, 2];
const array2 = ['U2', 'U1', 'U3'];
const result = [];
array1.sort(function(a,b){
return a-b;
})
array1.forEach((elem, index) => result.push([elem, array2[array2.indexOf('U'+elem)]]));
console.log(result);
If the prefix is not always U then this can be used:
const array1 = [1, 3, 2];
const array2 = ['U2', 'U1', 'U3'];
const result = [];
array1.sort(function(a,b){
return a-b;
})
array1.forEach((elem) => {
var array2Val = array2.find(val => val.indexOf(elem) !== -1);
result.push([elem, array2Val]);
});
console.log(result);
You could group the arrays by the numerical value of each item.
var array1 = [1, 3, 2],
array2 = ['U2', 'U1', 'U3'],
result = Object.values([array1, array2].reduce(
(o, a) => {
a.forEach(v => {
var k = v.toString().match(/\d+$/)[0];
(o[k] = o[k] || []).push(v);
});
return o;
},
Object.create(null)
));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Sorting a numeric array along with an array of objects in JavaScript

Suppose I generate two arrays
One that holds Array of numbers:
[5.65, 3.25, 4.34, 6.78]
And another array that holds objects with some information in them
[car.object1, car.object2, car.object3, car.object4]
And the objects in second array are related to the numbers in first array. So object1 is related to 5.65, object2 to 3.25 and so on.
So I want to sort the array 1 in an ascending order and at the same time sort the array 2 also.
So the result should be:
[3.25, 4.34, 5.65, 6.78]
&
[car.object2, car.object3, car.object1, car.object4]
My Approach: (You can just ignore the below answer as I think it is wrong. It does not work.)
var all = [];
var A = [5.65, 3.25, 4.34, 6.78];
var B = ['store.object1', 'store.object2', 'store.object3', 'store.object4'];
for (var i = 0; i < B.length; i++) {
all.push({
'A': A[i],
'B': B[i]
});
}
all.sort(function(a, b) {
return a.A - b.A;
});
A = [];
B = [];
for (var i = 0; i < all.length; i++) {
A.push(all[i].A);
B.push(all[i].B);
}
console.log(A, B);
You could use a temporary array with the indices and sort it with the values of the first array. Then map the sorted array with the values of array1 and array2.
I use strings for the second array, instead of missing objects.
var array1 = [5.65, 3.25, 4.34, 6.78],
array2 = ['car.object1', 'car.object2', 'car.object3', 'car.object4'],
temp = array1.map(function (_, i) { return i; });
temp.sort(function (a, b) { return array1[a] - array1[b]; });
array1 = temp.map(function (a) { return array1[a]; });
array2 = temp.map(function (a) { return array2[a]; });
console.log(temp);
console.log(array1);
console.log(array2);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Unless you want to implement the sort yourself, one simple way is to combine the entries from the number array with the entries from the object array (at least briefly), sort that, and then (if necessary) extract the result:
// Setup
var car = {
object1: {name: "object1"},
object2: {name: "object2"},
object3: {name: "object3"},
object4: {name: "object4"}
};
var nums = [5.65, 3.25, 4.34, 6.78];
var objs = [car.object1, car.object2, car.object3, car.object4];
// Combine
var joined = [];
nums.forEach(function(num, index) {
joined[index] = {num: num, object: objs[index]};
});
// Sort
joined.sort(function(a, b) {
return a.num - b.num;
});
// Extract
nums = [];
objs = [];
joined.forEach(function(entry, index) {
nums[index] = entry.num;
objs[index] = entry.object;
});
console.log(nums);
console.log(objs);
.as-console-wrapper {
max-height: 100% !important;
}
But rather than combine, sort, and extract, I'd probably just maintain a single array and add each number to its relevant object, so they always travel together.
Here is an ES6 way to do it:
let a = [5.65, 3.25, 4.34, 6.78];
let b = [{ x:1 }, { x:2 }, { x:3 }, { x: 4}];
[a, b] = a.map( (n, i) => [n, b[i]] ) // zip the two arrays together
.sort( ([n], [m]) => n-m ) // sort the zipped array by number
.reduce ( ([a,b], [n, o]) => [[...a, n], [...b, o]], [[],[]] ); // unzip
console.log(JSON.stringify(a));
console.log(JSON.stringify(b));
I've helped myself with an object containing car.object as the key and it's number as the value. Seems easy&quick solution.
var obj = [{'car.object1': 5.65}, {'car.object2': 3.25}, {'car.object3': 4.34}, {'car.object4': 6.78}],
objs = obj.sort((a,b) => a[Object.keys(a)] - b[Object.keys(b)]);
console.log(objs.map(v => Object.keys(v)[0]));
console.log(objs.map(v => v[Object.keys(v)]));
console.log(objs);
.as-console-wrapper {
max-height: 100% !important;
}

Categories

Resources