Remove sub-array contains common elements - javascript

As the title, if the input is [[1,2], [3,4], [1,3], [5,6], [6,5]], output should be [[1,2,3,4], [5,6]].
It's wrong on the recursive part. In my code, after running it, I will get [[1,2,3],[1,3,4],[5,6]], which means I need once more merge, but I'm confused how to continue the code until no sub-array contains common element.
Here is my code
function need_merge_or_not(arr)
{
for (var i = 0; i <= arr.length-1; i++) {
for (var j = i+1; j <= arr.length-1; j++) {
var arr_new = arr[i].concat(arr[j]);
//remove deplicates
var arr_merge = arr_new.filter(function (item, pos) {return arr_new.indexOf(item) == pos});
if (arr_merge.length < arr_new.length) {
return true;
}
}
}
return false;
}
function merge(arr)
{
if (arr.length >= 2) {
for (var i = 0; i <= arr.length-1; i++) {
for (var j = i+1; j <= arr.length-1; j++) {
var arr_new = arr[i].concat(arr[j]);
var arr_merge = arr_new.filter(function (item, pos) {return arr_new.indexOf(item) == pos});
if (arr_merge.length < arr_new.length) {
arr.splice(arr.indexOf(arr[i]), 1);
arr.splice(arr.indexOf(arr[j]),1);
arr.push(arr_merge);
}
}
if (need_merge_or_not(arr)) {
return merge(arr);
}
}
}
return arr;
}

I figured it out. Here is the code:
function merge(arr){
var input = [];
for(var i = 0; i < arr.length; i++){
input.push(arr[i]);
}
if (arr.length >= 2) {
for (var i = 0; i < arr.length; i++) {
for (var j = i+1; j < arr.length; j++) {
var arr_new = arr[i].concat(arr[j]);
//remove duplicates
var arr_merge = arr_new.filter(function (item, pos) {return arr_new.indexOf(item) == pos});
if (arr_merge.length < arr_new.length) {
arr.splice(arr.indexOf(arr[i]), 1, arr_merge);
arr.splice(arr.indexOf(arr[j]),1);
j--;
}
}
}
if (!arraysEqual(input, arr)) {merge(arr)};
}
return arr;
//Input:[[1,2], [3,4], [1,3], [5,6], [6,5]]
//Output:[[1,2,3,4], [5,6]]
}
function arraysEqual(a, b) {
if (a === b) return true;
if (a == null || b == null) return false;
if (a.length != b.length) return false;
for (var i = 0; i < a.length; ++i) {
if (a[i] !== b[i]) return false;
}
return true;
}

You could use two hash tables, one for the items and their groups and on for the result sets.
Basically the algorithm generates for the same group an object with a property and an array, because it allowes to keep the object reference while assigning a new array.
The main part is iterating the outer array and then the inner arrays and check inside, if it is the first item, then check the hash table for existence and if not exists, generate a new object with a values property and an empty array as value. Also assign the actual object to sets with item as key.
In a next step, the hash table is checked again and if not exist, then assign the object of the first element.
To maintain only unique values, a check is made and if the item does not exist, the item is pushed to the hash table's values array.
Then a part to join arrays follows by checking if the object of the first item is not equal to object of the actual item. If so, it delete from sets the key from the actual items's values first item and concat the array of the actual items to the first item's object's values. Then the values object is assigned to the actual item's object.
Later the sets are maped to the result set with iterating the sets object and the values property is taken as value.
var array = [[1, 2], [3, 4], [1, 3], [5, 6], [6, 5]],
groups = {},
sets = {},
result;
array.forEach(function (a) {
a.forEach(function (b, i, bb) {
if (i === 0 && !groups[b]) {
groups[b] = { values: [] };
sets[b] = groups[b];
}
if (!groups[b]) {
groups[b] = groups[bb[0]];
}
if (groups[b].values.indexOf(b) === -1) {
groups[b].values.push(b);
}
if (groups[bb[0]] !== groups[b]) {
delete sets[groups[b].values[0]];
groups[bb[0]].values = groups[bb[0]].values.concat(groups[b].values);
groups[b].values = groups[bb[0]].values;
}
});
});
result = Object.keys(sets).map(function (k) {
return sets[k].values;
});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Related

How to return the first occurrence of repeated item in an array using for loop?

I am trying to build logic currently with arrays and data structure. I am trying to implement the logic using for loop
function getRepeatingNumber(arr) {
for (var i = 0; i < arr.length; i++) {
for (var j = i + 1; j < arr.length; j++) {
if (arr[i] === arr[j]) {
return arr[i];
}
}
}
return undefined;
}
getRepeatingNumber([2, 3, 6, 5, 2]);
the above function takes in array and returns a repeated item in the array so in the above case it will return 2. But what if I have an array something like this arr[2,3,3,6,5,2] in this case it should return 3 but as the outer loop has index [0] which is 2 as the reference it will return 2 as the answer.
How to implement a function that returns the first occurrence of the repeated item.
Instead of iterating with j in the part after i, iterate the part before i:
function getRepeatingNumber(arr){
for (var i = 1; i < arr.length; i++) {
for (var j = 0; j < i; j++) {
if (arr[i] === arr[j]) {
return arr[i];
}
}
}
}
console.log(getRepeatingNumber([2,3,3,6,5,2]));
Note that an explicit return undefined is not needed, that is the default behaviour already.
You could also use indexOf to shorten the code a bit:
function getRepeatingNumber(arr){
for (var i = 1; i < arr.length; i++) {
if (arr.indexOf(arr[i]) < i) {
return arr[i];
}
}
}
console.log(getRepeatingNumber([2,3,3,6,5,2]));
You could even decide to make use of find -- which will return undefined in case of no match (i.e. no duplicates in our case):
function getRepeatingNumber(arr){
return arr.find((a, i) => {
if (arr.indexOf(a) < i) {
return true;
}
});
}
console.log(getRepeatingNumber([2,3,3,6,5,2]));
If you do this for huge arrays, then it would become important to have a solution that runs with linear time complexity. In that case, a Set will be useful:
function getRepeatingNumber(arr){
var set = new Set;
return arr.find(a => {
if (set.has(a)) return true;
set.add(a);
});
}
console.log(getRepeatingNumber([2,3,3,6,5,2]));
And if you are into functions of functions, and one-liners, then:
const getRepeatingNumber = r=>(t=>r.find(a=>[t.has(a),t.add(a)][0]))(new Set);
console.log(getRepeatingNumber([2,3,3,6,5,2]));
You need a data structure to keep track of first occurring index.
My recommendation is to use an array to store all the index of repeating numbers. Sort the array in ascending order and return the item at first index from the array.
function getRepeatingNumber(arr){
var resultIndexArr = [];
var count = 0;
var flag = 0;
for(var i=0;i<arr.length;i++)
{
for(var j=i+1;j<arr.length;j++)
{
if(arr[i] === arr[j])
{
flag = 1;
resultIndexArr[count++] = j;
}
}
}
resultIndexArr.sort((a, b) => a - b);
var resultIndex = resultIndexArr[0];
if(flag === 1)
return arr[resultIndex];
else
return;
}
console.log(getRepeatingNumber([2,3,6,5,2])); // test case 1
console.log(getRepeatingNumber([2,3,3,6,5,2])); // test case 2
console.log(getRepeatingNumber([2,5,3,6,5,2])); // test case 3
This will return correct result, but this is not the best solution. The best solution is to store your items in an array, check for each iteration if the item already exists in your array, if it exists then just return that item.
as a javascript dev you should be comfortable wit functional programming & higher-order functions so check the doc to get more understanding of some useful functions: like filter - find - reduce - findIndex map ...
Documentation
Now to answer your question:
at first you should think by step :
Get the occurrence of an item in an array as function:
const arr = [2, 5, 6, 2, 4, 5, 6, 8, 2, 5, 2]
const res = arr.reduce((numberofOcc, item) => {
if (item === 2)
numberofOcc++
return numberofOcc
}, 0);
console.log(`result without function ${res}`);
/* so my function will be */
const occurenceFun = (num, arr) => {
return arr.reduce((numberofOcc, item) => {
if (item === num)
numberofOcc++
return numberofOcc
}, 0);
}
console.log(`result using my function ${occurenceFun(2, arr)}`);
Now i have this function so i can use it inside another function to get the higher occurrence i have in an array
const arr = [1, 2, 5, 6, 8, 7, 2, 2, 2, 10, 10, 2]
const occurenceFun = (num, arr) => {
return arr.reduce((numberofOcc, item) => {
if (item === num)
numberofOcc++
return numberofOcc
}, 0);
}
/*let's create our function*/
const maxOccurenceFun = arr => {
let max = 0;
arr.forEach(el => {
if (max < occurenceFun(el, arr)) {
max = el
}
})
return max;
}
console.log(`the max occurence in this array is : ${maxOccurenceFun(arr)}`);

A simpler way to write the sum_pairs function

I want someone to show me a simpler way to write my sum_pairs(arr,sum) function which return the first 2 values in arr that adds up to form sum . My code works but i think it is complex , i need someone to simplify it . So, this is my code .
function sum_pairs(ints,s){
let arr=[];
let arrOfIndex=[];
for(let i=0;i<ints.length;i++){
for(let a=0;a<ints.length;a++){
if(a!=i){
if(ints[i]+ints[a]==s){
let newArr=[ints[i],ints[a]];
let sumIndex=i+a;
arr.push(newArr);
arrOfIndex.push(sumIndex);
}
}
}
}
let sortedArray=arrOfIndex.sort((a,b)=>a-b);
return arr[arrOfIndex.indexOf(sortedArray[0])];
}
console.log(sum_pairs([7,2,5,8,4,3],7))//[2,5]
You could take a single loop with a hash table for the second pair element.
It works by looking up the actual value and if found, then this value is part of a pair. In this case return the delta of the sum and the value and the value.
If not found, add a new entry to the hash table with the missing value for getting a sum.
Proceed until found or end.
function sum_pairs(ints, s) {
var hash = Object.create(null),
i,
value;
for (i = 0; i < ints.length; i++) {
value = ints[i];
if (hash[value]) return [s - value, value];
hash[s - value] = true;
}
}
console.log(sum_pairs([7, 2, 5, 8, 4, 3], 7));
All pairs (with an array without duplicates)
function allPairs(ints, s) {
var hash = Object.create(null),
i,
value,
pairs = [];
for (i = 0; i < ints.length; i++) {
value = ints[i];
if (hash[value]) pairs.push([s - value, value]);
hash[s - value] = true;
}
return pairs;
}
console.log(allPairs([7, 2, 5, 8, 4, 3], 7));
Finally find duplicate pairs as well :-)
function allPairs(ints, s) {
var hash = Object.create(null),
i,
value,
pairs = [];
for (i = 0; i < ints.length; i++) {
value = ints[i];
if (hash[value]) {
pairs.push([s - value, value]);
hash[value]--;
continue;
}
if (!hash[s - value]) {
hash[s - value] = 0;
}
++hash[s - value];
}
return pairs;
}
console.log(allPairs([4, 3, 3, 4, 7, 2, 5, 8, 3], 7));
Steps
Iterate through the array
Find the composition of n (n is the number in each interation) by using this equation n_composition = sum - n
Search for n_composition in the array
If found return [n, n_comp]. If not then continue the loop. If not found at all then return null.
let n = 0;
let n_comp = 0;
let sum_pairs = (arr, sum) => {
for(let i = 0, len = arr.length; i < len; ++i){
n = arr[i];
n_comp = sum - n;
if (arr.includes(n_comp)){
return [n, n_comp];
}
}
return null;
}
console.log(sum_pairs([7,2,5,8,4,3],7))//[2,5]
You can use the following code
function sum_pairs(ints, s) {
let results = [];
for (let i=0; i<ints.length; i++) {
for (let j=i+1; j<ints.length; j++) {
if (ints[j] === s - ints[i]) {
results.push([ints[i], ints[j]])
}
}
}
return results;
}
This will provide you all the pairs
To return a single pair you can use following
function sum_pairs(ints, s) {
let results = [];
for (let i=0; i<ints.length; i++) {
for (let j=i+1; j<ints.length; j++) {
if (ints[j] === s - ints[i]) {
results.push([ints[i], ints[j]])
return results;
}
}
}
}

Javascript delete array element from array by value

I have an javascript array and I want to delete an element based on the value of the array, this is my array and this is what I have tried without success.
array = []
array.push (["Mozilla","Firefox",1.10])
index = array.indexOf(["Mozilla","Firefox",1.10])
array.splice(index, 1)
But it doesn't work, any idea¿?
You're trying to compare arrays, which are objects and have unique addresses. Your index variable is -1.
Try ['Mozilla','Firefox',1.10] === ['Mozilla','Firefox',1.10] in your console, you'll see that just because two arrays have the same values, it doesn't mean they are the same array.
What you need is a deep-equals style of comparison, that checks each value in the array, to see if two arrays have a likeness.
Take a look at lodash's isEqual function for an idea.
Here's a simple looping function:
function deepIndex(array, comparison) {
var i, j;
main:
for (i = 0; i < array.length; i++) {
if (Array.isArray(array[i])) {
for (j = 0; j < array[i].length; j++) {
if (array[i][j] !== comparison[j]) {
continue main;
}
}
return i;
}
}
}
var arr = [];
arr.push('string', ['Mozilla','Firefox',1.10], 'thing');
var index = deepIndex(arr, ['Mozilla','Firefox',1.10])
console.log(index, arr);
arr.splice(index, 1);
console.log(arr);
Take a look at this:
// Array Remove - By John Resig (MIT Licensed)
Array.prototype.remove = function(from, to) {
var rest = this.slice((to || from) + 1 || this.length);
this.length = from < 0 ? this.length + from : from;
return this.push.apply(this, rest);
};
This is function, made by the Creator of JQUery.
Basically you take the Index of one thing and than it is getting removed
Array.prototype.remove = function(from, to) {
var rest = this.slice((to || from) + 1 || this.length);
this.length = from < 0 ? this.length + from : from;
return this.push.apply(this, rest);
};
//Equals Function taken from:
//http://stackoverflow.com/questions/7837456/comparing-two-arrays-in-javascript
Array.prototype.equals = function (array) {
// if the other array is a falsy value, return
if (!array)
return false;
// compare lengths - can save a lot of time
if (this.length != array.length)
return false;
for (var i = 0, l=this.length; i < l; i++) {
// Check if we have nested arrays
if (this[i] instanceof Array && array[i] instanceof Array) {
// recurse into the nested arrays
if (!this[i].equals(array[i]))
return false;
}
else if (this[i] != array[i]) {
// Warning - two different object instances will never be equal: {x:20} != {x:20}
return false;
}
}
return true;
}
array = [];
array.push (["Mozilla","Firefox",1.10]);
array.push (["Microsoft","Spartan",1.0]);
array.push (["Safari","Safari",1.4]);
index = indexOfArr(array,["Mozilla","Firefox",1.10]);
array.remove(index, index);
document.getElementById("length").innerHTML = array.length;
for(var i = 0; i < array.length; i++){
document.getElementById("elems").innerHTML += "<br>"+array[i];
}
function indexOfArr(hay, needle){
for(var i = 0; i < hay.length; i++){
if (hay[i].equals(needle)){
return i;
}
}
return -1;
}
<span id = "length"></span><br>
<span id = "elems">Elements:</span>
You can use the fiter metodh, instead of indexOf.
Within the callback of that method, you can choose different approaches:
Use toString on the arrays and compare the two strings
Test for the length and the content, by iterating over the contained elements
... Continue ...
In any case using === will solve the problem, unless the object contained is exactly the same against which you are trying to match.
By the same, I mean the same. We are non speaking about having the same content, but to be the same instance.
Loop over your array and check the equality:
array = [];
array.push(["Mozilla", "Firefox", 1.10]);
for (var i = 0; i < array.length; i++) {
if (arraysEqual(array[i], ["Mozilla", "Firefox", 1.10])) {
array.splice(i, 1);
}
}
function arraysEqual(a, b) {
if (a === b) return true;
if (a === null || b === null) return false;
if (a.length != b.length) return false;
for (var i = 0; i < a.length; ++i) {
if (a[i] !== b[i]) return false;
}
return true;
}
JSFiddle: http://jsfiddle.net/ghorg12110/r67jts35/
Based on this question : How to check if two arrays are equal with JavaScript?
You can do something like this
array = []
array.push (["Mozilla","Firefox",1.10])
tempArray = array[0];
index = tempArray.indexOf("Mozilla","Firefox",1.10)
array.splice(index, 1)
You can build on this if you put for loop instead of hard coding.

Uniqualize array in Javascript? [duplicate]

This question already has answers here:
Remove duplicate values from JS array [duplicate]
(54 answers)
Closed 8 years ago.
I have the following script that's supposed to uniqualize array:
function uniques(arr) {
var a = [];
for (var i=0, l=arr.length; i<l; i++)
for (var j = 0; j < arr[i].length; j++) {
if (a.indexOf(arr[i][j]) === -1 && arr[i][j] !== '') {
a.push(arr[i][j]);
}
}
return a;
}
However, when it receives only one element, it just breaks it into the letters (which is understandable).
Do you know how I make it check if it's receiving only one element and then returns it back?
Thanks!
I'm not sure why you need the nested loop - you only need a single loop if you're processing a non-nested array:
function uniques(arr) {
if (arr.length === 1) { return arr };
var a = [];
for (var i = 0, l = arr.length; i < l; i++) {
if (a.indexOf(arr[i]) === -1) {
a.push(arr[i]);
}
}
return a;
}
DEMO
If you want to process nested arrays, use a recursive function. Here I've used underscore's flatten method as the basis:
function toType(x) {
return ({}).toString.call(x).match(/\s([a-zA-Z]+)/)[1].toLowerCase();
}
function flatten(input, output) {
if (!output) { output = []; }
for (var i = 0, l = input.length; i < l; i++) {
var value = input[i];
if (toType(value) !== 'array' && output.indexOf(value) === -1) {
output.push(value);
} else {
flatten(value, output);
}
}
return output.sort(function (a, b) { return a - b; });
};
var arr = [1, 2, 3, [[4]], [10], 5, 1, 3];
flatten(arr); // [ 1, 2, 3, 4, 5, 10 ]
DEMO
I think the solution is this:
function uniques(arr) {
if (arr.length > 1) {
var a = [];
for (var i=0, l=arr.length; i<l; i++)
for (var j = 0; j < arr[i].length; j++) {
if (a.indexOf(arr[i][j]) === -1 && arr[i][j] !== '') {
a.push(arr[i][j]);
}
}
return a;
}
else
{
return arr;
}
}
What you're trying to do is a linear search on the created matrix for each item in the original one.
The solution below will accomplish this, but at a great cost. If your original matrix is 50x50 with unique values in each cell, it will take you 50^3 (=125000) loops to exit the function.
The best technique to search, in programming science, takes O(log(N)) that means that if you'll use it on your problem it will take log(50^2) (=11) loops.
function uniques(arr) {
var items = [];
var a = arr.map(function(row, i) {
return row.map(function(cell, j) {
if (items.indexOf(cell) >= 0) {
items.push(cell);
return cell;
}
});
});
}

Finding missing array in array of arrays

I need to find a missing array in an "array of arrays". I started by finding this function below (on StackOverflow):
function findDeselectedItem(CurrentArray, PreviousArray) {
var CurrentArrSize = CurrentArray.length;
var PreviousArrSize = PreviousArray.length;
var deselectedItem = [];
// loop through previous array
for(var j = 0; j < PreviousArrSize; j++) {
// look for same thing in new array
if (CurrentArray.indexOf(PreviousArray[j]) == -1)
deselectedItem.push(PreviousArray[j]);
}
return deselectedItem;
}
This works just fine if you did something like this:
oldarray = ["hi", "ho", "hey"];
newarray = ["hi", "hey"];
Using findDeselectedItem(newarray, oldarray) would return ["ho"].
However, my content looks like this:
oldarray = [["James", 17, 1], ["Olivia", 16, 0], ["Liam", 18, 1]];
newarray = [["Olivia", 16, 0], ["James", 17, 1]];
How can I adapt the function above so that it returns the missing array containing 'Liam'.
Thanks
I would make a hash with the name as a key. That would make finding missing content trivial and very fast. You can then optimize the method by not rebuilding the hash every time, but only when it's really necessary.
var oldArray = [["James", 17, 1], ["Olivia", 16, 0], ["Liam", 18, 1]];
var newArray = [["Olivia", 16, 0], ["James", 17, 1]];
function findDeselectedItems(oldArray, newArray)
{
var results = [];
var hash = {};
for (var i=0; i<newArray.length; i++) {
hash[newArray[i].join(',')] = true;
}
for (var i=0; i<oldArray.length; i++) {
if (!hash[oldArray[i].join(',')]) {
results.push(oldArray[i]);
}
}
return results;
}
The problem may be that indexOf uses strict equality. I.e. if an item in the 'previous' array isn't literally also in the 'current' array, it will report it to not be in there.
You will have to iterate over the values yourself (instead of using indexOf) and check if the array contains something that is 'the same as' (but not literally the same) the array.
I.e. if I didn't explain myself well enough take a look at this;
['bob'] == ['bob']; //false
//therefore
[['bob']].indexOf(['bob']); //-1
I hope that this helps you,
function findDeselectedItem(CurrentArray, PreviousArray) {
var CurrentArrSize = CurrentArray.length;
var PreviousArrSize = PreviousArray.length;
var deselectedItem = [];
// loop through previous array
for(var j = 0; j < PreviousArrSize; j++) {
var checkArray = PreviousArrSize[j];
// loop through 2nd array to match both array
for(var i = 0; i < CurrentArrSize; i++) {
// look for same thing in new array
if (CurrentArray[i].indexOf(checkArray) == -1)
deselectedItem.push(CurrentArray[i]);
}
}
return deselectedItem;
}
#KarelG: nice and quick solution but should it not be var checkArray = PreviousArr[j]; instead of var checkArray = PreviousArrSize[j]; ?
function findDeselectedItem(CurrentArray, PreviousArray) {
var CurrentArrSize = CurrentArray.length;
var PreviousArrSize = PreviousArray.length;
var deselectedItem = [];
var selectedIndices = [];
// loop through previous array
for(var j = 0; j < PreviousArrSize; j++) {
for(k=0; k < CurrentArrSize ; k++){
if (CurrentArray[k].toString() === PreviousArray[j].toString()){
selectedIndices.push(j);
break;
}
}
}
for(var l = 0; l < PreviousArrSize; l++){
if(selectedIndices.indexOf(l) === -1){
deselectedItem.push(PreviousArray[l]);
}
}
return deselectedItem;
}
I don't think you can use indexOf to compare two arrays. You need a deeper comparison. Although this code could be written another way, you could do this with an array comparison function and using Array.some() to filter through your elements. Here's an example and a fiddle;
// Credit http://stackoverflow.com/questions/7837456/comparing-two-arrays-in-javascript
// attach the .compare method to Array's prototype to call it on any array
Array.prototype.compare = function (array) {
// if the other array is a falsy value, return
if (!array)
return false;
// compare lengths - can save a lot of time
if (this.length != array.length)
return false;
for (var i = 0; i < this.length; i++) {
// Check if we have nested arrays
if (this[i] instanceof Array && array[i] instanceof Array) {
// recurse into the nested arrays
if (!this[i].compare(array[i]))
return false;
}
else if (this[i] != array[i]) {
// Warning - two different object instances will never be equal: {x:20} != {x:20}
return false;
}
}
return true;
}
function findDeselectedItem(CurrentArray, PreviousArray) {
var CurrentArrSize = CurrentArray.length;
var PreviousArrSize = PreviousArray.length;
var deselectedItem = [];
// loop through previous array
for (var j = 0; j < PreviousArrSize; j++) {
// look for same thing in new array
CurrentArray.some(function (a, idx) {
if(PreviousArray[j].compare(a) == false) {
deselectedItem.push(PreviousArray[j]);
return true;
}
});
}
return deselectedItem;
}
var oldarray =[["James", 17, 1], ["Olivia", 16, 0], ["Liam", 18, 1]];
var newarray =[["Olivia", 16, 0], ["James", 17, 1]];
console.log(findDeselectedItem(newarray, oldarray));

Categories

Resources