Find consecutive numbers in multiples arrays JavaScript - javascript

I have array structured like this:
[
[8,[1]],
[15,[2]],
[20,[3]],
[23,[4,41]],
[497,[18]],
[1335,[38]],
[2092,[39,55,61]],
[3615,[5]],
[4121,[14]],
[5706,[39,55,61]],
[5711,[62]],
[5714,[63]],
[5719,[64]],
[6364,[38]]
]
I use the modified code from this answer to find consecutive numbers but I can't adapt it to also find consecutive numbers from arrays with multiple values
This is my code :
const a = [
[8,[1]],
[15,[2]],
[20,[3]],
[23,[4,41]],
[497,[18]],
[1335,[38]],
[2092,[39,55,61]],
[3615,[5]],
[4121,[14]],
[5706,[39,55,61]],
[5711,[62]],
[5714,[63]],
[5719,[64]],
[6364,[38]]
];
// this variable will contain arrays
let finalArray = [];
// Create a recursive function
function checkPrevNextNumRec(array) {
let tempArr = [];
// if the array contaon only 1 element then push it in finalArray and
// return it
if (array.length === 1) {
finalArray.push(array);
return
}
// otherside check the difference between current & previous number
for (let i = 1; i < array.length; i++) {
if (array[i][1][0] - array[i - 1][1][0] === 1) {
// if current & previous number is 1,0 respectively
// then 0 will be pushed
tempArr.push(array[i - 1]);
} else {
// if current & previous number is 5,2 respectively
// then 2 will be pushed
tempArr.push(array[i - 1])
// create a an array and recall the same function
// example from [0, 1, 2, 5, 6, 9] after removing 0,1,2 it
// will create a new array [5,6,9]
let newArr = array.splice(i);
finalArray.push(tempArr);
checkPrevNextNumRec(newArr)
}
// for last element if it is not consecutive of
// previous number
if (i === array.length - 1) {
tempArr.push(array[i]);
finalArray.push(tempArr)
}
}
}
checkPrevNextNumRec(a)
And here the result, as you can see, all the tables containing consecutive figures in [i][1][0] have been grouped
[
[
[8,[1]],
[15,[2]],
[20,[3]],
[23,[4,41]]
],
[
[497,[18]]
],
[
[1335,[38]],
[2092, [39,55,61]]
],
[
[3615,[5]]
],
[
[4121,[14]]
],
[
[5706,[39,55,61]]
],
[
[5711,[62]],
[5714,[63]],
[5719,[64]]
],
[
[6364,[38]]
]
]
But I need that field 5706 is also included with 5711, 5714, and 5719, but obviously it is not included because is not in [i][1][0]
I thought of being inspired by this post but I cannot integrate it correctly
Can you help me?
Thanks!

The following code enumerates each of the items in the array.
For each item, its sub-array is enumerated to see if the following item fits in a sequence.
If it fits in sequence, the next item is appended to a temporary variable, and we continue to the next item.
If the last entry in the subarray is reached without detecting a continuation of the sequence, we end the current sequence, start a new sequence and continue to the next item.
const data = [ [8,[1]], [15,[2]], [20,[3]], [23,[4,41]], [497,[18]], [1335,[38]], [2092,[39,55,61]], [3615,[5]], [4121,[14]], [5706,[39,55,61]], [5711,[62]], [5714,[63]], [5719,[64]], [6364,[38]] ]
function sequences(data) {
const result = []
let sequence = [data[0]]
for(let x=0; x<data.length-1; x++) {
const [,sub] = data[x]
for(let y=0; y<sub.length; y++) {
const currSubV = sub[y]
const [,nextSub] = data[x+1]
if(nextSub.some((nextSubV) => currSubV+1 === nextSubV)) {
sequence.push(data[x+1])
break
}
if(y === sub.length-1) {
result.push(sequence)
sequence = [data[x+1]]
}
}
}
return result
}
for(let s of sequences(data)) console.log(JSON.stringify(s).replace(/\s/g))

Basically the logic here is to iterate over the input and at each iteration, compare it with the data values from the previous iteration i.e.:prevData.
On each iteration, I used Array.prototype.map to create a new array that contains the what-would-be consecutive values (relative to the data in the previous iteration) by simply adding 1 to each item in prevData.
Next step is to loop that array to see if we encounter a consecutive value - as soon as we do we can break as there's no need to keep checking.
Finally is we apply the grouping logic with a single if/else.
const a = [
[8, [1]],
[15, [2]],
[20, [3]],
[23, [4, 41]],
[497, [18]],
[1335, [38]],
[2092, [39, 55, 61]],
[3615, [5]],
[4121, [14]],
[5706, [39, 55, 61]],
[5711, [62]],
[5714, [63]],
[5719, [64]],
[6364, [38]]
];
function group(input) {
let prev;
return input.reduce((accum, el) => {
let hasConsecutive = false;
const [key, current] = el;
if (prev == null) {
prev = current;
}
const consecutives = prev.map(n => n + 1);
for (let i = 0; i < consecutives.length; i += 1) {
if (current.includes(consecutives[i])) {
hasConsecutive = true;
break;
}
}
if (prev && hasConsecutive) {
accum[accum.length - 1].push(el);
} else {
accum.push([el]);
}
prev = current;
return accum;
}, []);
}
console.log(group(a));
Here's the result run through a beautifier:
[
[
[8, [1]],
[15, [2]],
[20, [3]],
[23, [4, 41]]
],
[
[497, [18]]
],
[
[1335, [38]],
[2092, [39, 55, 61]]
],
[
[3615, [5]]
],
[
[4121, [14]]
],
[
[5706, [39, 55, 61]],
[5711, [62]],
[5714, [63]],
[5719, [64]]
],
[
[6364, [38]]
]
]

You need to check the last element of the grouped value.
const
array = [[8, [1]], [15, [2]], [20, [3]], [23, [4, 41]], [497, [18]], [1335, [38]], [2092, [39, 55, 61]], [3615, [5]], [4121, [14]], [5706, [39, 55, 61]], [5711, [62]], [5714, [63]], [5719, [64]], [6364, [38]]],
grouped = array.reduce((r, a) => {
var last = r[r.length - 1];
if (!last || last[last.length - 1][1][0] + 1 !== a[1][0]) r.push(last = []);
last.push(a);
return r;
}, []);
console.log(grouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }

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

Identifying the index of array against the target value

One of the challanging question I got in office, which i could not able to come out of it. need the help here.
const array = [2, 7, 11, 15], target = 9;
in the above i have an array and target as 9, as well this target can change any of value as 18,26 like so. the result should show the indexOf array which used for get the target. for example at present it is 9, so the result should be [0,1] (2+7). if the target is 26 then result should be [2,3]. how to achieve this?
for my try the first attempt is working. but rest of them not. need the help.
my code :
const array = [2, 7, 11, 15], target = 9;
const result = [];
const outPut = array.reduce((c,v,i,a) => {
if(c !== target && c < target) {
result.push(a.indexOf(v));
}
return c + v;
}, 0);
console(result);
Here's the brute force solution:
Get all the subsets of the array.
Compute the sum of each subset.
Filter the subsets to those whose sum is the target.
const array = [1, 2, 7, 8, 9, -2];
const target = 9;
const getAllSubsets = array => array.reduce(
(subsets, value) => subsets.concat(subsets.map(set => [...set, value])),
[[]]
);
const subsets = getAllSubsets(array);
const result = subsets.filter(
subset => subset.reduce((partialSum, element) => partialSum + element, 0) == target
);
console.log(result);
This example produces all the subsets of [1, 2, 7, 8, 9, -2] that sum to 9.
Output:
[ [ 2, 7 ], [ 1, 8 ], [ 9 ], [ 1, 2, 8, -2 ], [ 2, 9, -2 ] ]
You only need to make two small changes to make this work with indices instead of the actual values:
get all subsets of array.map((_, i) => i) instead of array to get the indices
sum using array[element] instead of element.
const array = [1, 2, 7, 8, 9, -2];
const target = 9;
const getAllSubsets = array => array.reduce(
(subsets, value) => subsets.concat(subsets.map(set => [...set, value])),
[[]]
);
const subsets = getAllSubsets(array.map((_, i) => i));
const result = subsets.filter(
subset => subset.reduce((partialSum, element) => partialSum + array[element], 0) == target
);
console.log(result);
The problem with this approach is that you might end up adding the wrong elements; for example, if target was 13, your code would first add 2 and 7 and then not return the correct result because it wouldn't then consider adding 11 since that exceeds the target. Instead, you should use a two-pointer technique (see https://www.geeksforgeeks.org/two-pointers-technique/)
const array = [2, 7, 11, 15];
let result = [];
const getResult = (target) => {
for(let i =0; i < array.length;i++){
let requiredValue = target - array[i];
if(array.indexOf(requiredValue) > -1) {
result = [array.indexOf(array[i]), array.indexOf(requiredValue)].sort();
}
if(array.indexOf(requiredValue) < -1) {
result = [0];
}
}
}
getResult(9)
console.log(result);
getResult(18)
console.log(result);
getResult(26)
console.log(result);
Here is my solution:
const array = [2, 7, 11, 15];
const target = 26;
let result = [];
for(let i =0; i < array.length;i++){
let requiredValue = target - array[i];
if(array.indexOf(requiredValue) > -1) {
result = [array.indexOf(array[i]), array.indexOf(requiredValue)].sort();
}
}
console.log(result);
it works for me. any one find the issue, pls comment. Change the target and play.

create arrays of intersecting values

Here are some arrays of values:
values = [
[1,2,3],
[2,3,4],
[8,9,10],
[9,10,11],
[13,14,15]
];
I want to create new numerically sorted arrays of the union of arrays' values when there is an intersection of the values of two or more arrays.
Values in these new sorted arrays will be unique.
If an array does not intersect any other arrays, then we include that array in the results (e.g. [13,14,15] in the example).
For example:
clusters = [
[1,2,3,4],
[8,9,10,11],
[13,14,15]
];
Since value[0] and value[1] intersect, we add a union of their values to clusters.
Since value [2] and value[3] intersect, we add a union of their values to clusters.
Since value[4] does not intersect value[0] through value[4], we just add value[5] to clusters.
Now, if there was a value[6] = [3, 100], then our clusters would look like this:
clusters = [
[1,2,3,4,100],
[8,9,10,11],
[13,14,15]
];
because value[6] intersected value[0] and value[1], so we add to their union.
Is there a technique or optimal way to do this?
In my example, the original arrays are sorted, but that might not necessarily be the case.
Here is an edited snippet in response to the comments using .reduceRight(), seeding the accumulator with a copy of the passed array, and still using some() and includes() to find duplicates.
reduceRight() iterates the array in reverse, while findIndex() searches from the beginning. When a match is found the current iterated array is pushed to the matched array and then the current element is removed from the accumulator using splice().
function clusterDuplicates(arr) {
return arr
.reduceRight((a, arr, i) => {
if (i) {
let j = a.slice(0, i).findIndex(_arr => arr.some(x => _arr.includes(x)));
if (~j) {
a[j].push(...arr);
a.splice(i, 1);
}
}
return a
}, [...arr])
.map(arr => [...new Set(arr)].sort((a, b) => a - b));
}
console.log(clusterDuplicates([[1, 2, 3], [3, 4, 2], [8, 9, 10], [9, 11, 10], [14, 13, 15]]));
console.log(clusterDuplicates([[1, 2], [3, 4], [2, 3]]));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Original Answer
As noted in the comments, this fails to look ahead for duplicates.
Here's a fairly concise implementation using reduce() looking for intersections using some() and includes(). The result is then mapped to remove duplicates using Sets and then sorted.
const
values = [[1, 2, 3], [3, 4, 2], [8, 9, 10], [9, 11, 10], [14, 13, 15]],
result =
values
.reduce((a, arr) => {
let i = a.findIndex(_arr => arr.some(x => _arr.includes(x)));
if (i === -1) {
i = a.push([]) - 1;
}
a[i].push(...arr);
return a
}, [])
.map(arr => [...new Set(arr)].sort((a, b) => a - b));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
To see if 2 arrays intersect, a nice an simple way is to compare the set of both arrays together's size, with each array's set size, and if there different we know they intersect.
Below is an example..
const values = [
[1,2,3],
[8,9,10],
[2,3,4],
[9,10,11],
[13,14,15]
];
function arrayItersect(a,b) {
return new Set([...a,...b]).size !==
new Set(a).size + new Set(b).size;
}
function joinIntersections(v) {
const a = [...v]; //make a copy
for (let l = 0; l < a.length-1; l += 1) {
let l2 = l + 1;
while (l2 < a.length) {
if (arrayItersect(a[l], a[l2])) {
a[l] =
[...new Set(
[...a[l],...a.splice(l2, 1)[0]]
)];
} else l2 ++;
}
}
return a;
}
console.log(joinIntersections(values));
Use for-loop and for each sub array check with previous array (last) whether it has intersection. If intersection, add the merged array to result.
values = [
[1, 2, 3],
[2, 3, 4],
[8, 9, 10],
[9, 10, 11],
[13, 14, 15],
];
const intersect = (arr1, arr2) => {
const both = [...arr1, ...arr2];
const uniq = [...new Set(both)];
return uniq.length !== both.length ? uniq : null;
};
const compact = (arrArr) => {
if (arrArr?.length < 2) {
return arrArr;
}
const res = [];
let last = arrArr[0];
for (let i = 1; i < arrArr.length; i++) {
last = intersect(last, arrArr[i]) ?? (res.push(last), arrArr[i]);
}
res.push(last);
return res;
};
console.log(compact(values))

How to ensure a js array respects element order when I add them to a containing array?

I am looping thru an array and trying to find where two elements sum to 10.
I find the correct match pairs... but for two of them the order is not retained when they are added.
I expect to get
[ [9,1], [6,4], [3,7], [7,3], [6,4], [1,9] ]
but I get
[ [9,1], [6,4], [3,7], [3,7], [6,4], [9,1] ]
i.e. => ^^^ ^^^
are different
Why do the two pairs indicated have their order backwards ?
newNums = [];
nums.forEach(num1 => {
nums.forEach(num2 => {
num1Position = nums.indexOf(num1);
num2Position = nums.indexOf(num2);
if (num1 + num2 === 10 && num2Position > num1Position ) {
newNums.push([num1, num2]);
}
})
})
return newNums;
}
result = sumTwoNumbersIsTen([9,6,3,7,3,6,4,2,0,1,9])
console.log(result); // should be [ [9,1], [6,4], [3,7], [7,3], [6,4], [1,9] ]
// but I get [ [9,1], [6,4], [3,7], [3,7], [6,4], [9,1] ]
//
You could iterate with indices and omit parts who are not valid.
BTW, it is a good idea to declare all variables.
const sumTwoNumbersIsTen = nums => {
const newNums = [];
for (let i = 0; i < nums.length - 1; i++) {
const num1 = nums[i];
for (let j = i + 1; j < nums.length; j++) {
const num2 = nums[j];
if (num1 + num2 === 10) {
newNums.push([num1, num2]);
}
}
}
return newNums;
};
console.log(sumTwoNumbersIsTen([9, 6, 3, 7, 3, 6, 4, 2, 0, 1, 9])); // [[9, 1], [6, 4], [3, 7], [7, 3], [6, 4], [1, 9]]
Because you're using the wrong positions. Instead of taking the position of the element from the current iteration, which forEach passes to your callback, you are searching for an index in the array where the value could be found. For duplicate values, this will always find the first index, not the one you want (and also it's horribly inefficient).
newNums = [];
nums.forEach((num1, num1Position) => {
nums.forEach((num2, num2Position) => {
if (num1 + num2 === 10 && num2Position > num1Position ) {
newNums.push([num1, num2]);
}
})
})
The problem is that there are (for example) two 9s in the array, and .indexOf only finds the index of the first occurence.
You can solve this by not using indexOf and instead exploiting that the callback to forEach can take a second parameter which is the element's index:
function sumTwoNumbersIsTen(nums) {
newNums = [];
nums.forEach((num1, num1Position) => {
nums.forEach((num2, num2Position) => {
if (num1 + num2 === 10 && num2Position > num1Position ) {
newNums.push([num1, num2]);
}
})
})
return newNums;
}
result = sumTwoNumbersIsTen([9,6,3,7,3,6,4,2,0,1,9])
console.log(result);

Check array overlapping in JavaScript

I have some arrays like [1,5], [3,6], [2,8],[19,13], [12,15]. When i pass two arrays in the function output will be [1,6], [2,19],[12,15]
i want to remove overlapping numbers from 2 arrays . like on fist and second array 5 and 3 will be overlap between 1 to 6.
I believe this is what you want (you get the min of the first array and the max of the second array):
function removeOverlap(arr1, arr2) {
if (arr1 === undefined) {
return arr2;
}
if (arr2 === undefined) {
return arr1;
}
return [Math.min.apply(null, arr1), Math.max.apply(null, arr2)];
}
// Sample:
var myArrays = [[1,5], [3,6], [2,8], [19,13], [12,15]];
for (var i = 0; i < myArrays.length; i = i + 2) {
console.log(removeOverlap(myArrays[i], myArrays[i + 1]));
}
EDIT: answer with multiple parameters as you requested in your comment:
We could use rest parameters in the answer below, but I will use the arguments object for compatibility with Internet Explorer. If this is not a requirement you can adapt the solution to use the first.
function removeOverlap(arr1, arr2) {
// Converting the arguments object to array:
var argsArray = Array.prototype.slice.call(arguments);
// Removing undefined:
argsArray = argsArray.filter(function(el) {
return el != undefined;
});
// Alternative (not compatible with Internet Explorer):
//argsArray = argsArray.filter(el => el);
// We're looking for the min and max numbers, let's merge the arrays
// e.g. transform [[1, 5], [3, 6], [2, 8]] into [1, 5, 3, 6, 2, 8]
var merged = [].concat.apply([], argsArray);
// Alternative, but it is not compatible with Internet Explorer:
//var merged = Array.flat(argsArray);
return [Math.min.apply(null, merged), Math.max.apply(null, merged)];
}
// Sample:
var myArrays = [[1,5], [3,6], [2,8], [19,13], [12,15]];
for (var i = 0; i < myArrays.length; i = i + 2) {
console.log(removeOverlap(myArrays[i], myArrays[i + 1]));
}
console.log(removeOverlap(myArrays[0], myArrays[1], myArrays[2]));
This can easily be accomplished my finding the min of the current and max of the next item.
let initial = [ [1, 5], [3, 6], [2, 8], [19, 13], [12, 15] ]
let expected = [ [1, 6], [2, 19], [12, 15] ]
let actual = calculateOverlaps(initial);
console.log(JSON.stringify(actual) === JSON.stringify(expected)); // true
function calculateOverlaps(arr) {
let result = [];
for (let i = 0; i < arr.length; i+=2) {
if (i >= arr.length - 1) {
result.push(arr[i]); // If the array has an odd size, get last item
} else {
let curr = arr[i];
let next = arr[i + 1];
result.push([ Math.min(...curr), Math.max(...next) ]);
}
}
return result;
}
Here is a more code-golf oriented function:
const calculateOverlaps1 = (arr) => arr.reduce((r, e, i, a) =>
(i % 2 === 0)
? r.concat([
(i < a.length - 1)
? [ Math.min(...e), Math.max(...a[i+1]) ]
: e
])
: r, []);
And even smaller, at just 101 bytes.
f=a=>a.reduce((r,e,i)=>i%2===0?r.concat([i<a.length-1?[Math.min(...e),Math.max(...a[i+1])]:e]):r,[]);

Categories