First common element across multiple arrays in Javascript - javascript

I need to find the first common element across a group of arrays. The number of arrays may vary, but they are always in sequential order (small->large). My arrays are all properties of myObj.
This is what I have so far:
function compare(myObj,v,j) {
if (myObj[j].indexOf(v)>-1) return true;
else return false;
}
function leastCommon ([1,5]) {
var myObj = { //This is filled by code, but the finished result looks like this
1: [1, 2,...,60,...10k]
2: [2, 4,...,60,...20k]
3: [3, 6,...,60,...30k]
4: [4, 8,...,60,...40k]
5: [5,10,...,60,...50k]
};
var key = [1,2,3,4,5]; //also filled by code
var lcm = myObj[key[key.length-1]].forEach(function(v) { //Iterate through last/largest multiple array
var j=key[key.length-2];
while (j>=0) {
if (compare(myObj,v,j)) { //check to see if it is in the next lower array, if yes, check the next one.
j--;
}
if (j>0 && (compare(myObj,v,j+1))===true) return v; //before the loop exits, return the match
}
});
return lcm;
}
I'm not sure what is wrong, but it is returning undefined.
Note: yes, I know a forEach returns undefined, and I tried modifying my code, and I get a "potential infinite loop" error from my editor. Modified code looks like this:
function leastCommon ([1,5]) {
var myObj = { //This is filled by code, but the finished result looks like this
1: [1, 2,...,60,...10k]
2: [2, 4,...,60,...20k]
3: [3, 6,...,60,...30k]
4: [4, 8,...,60,...40k]
5: [5,10,...,60,...50k]
};
var key = [1,2,3,4,5]; //also filled by code
var lcm = 0;
myObj[key[key.length-1]].forEach(function(v) { //Iterate through last/largest multiple array
var j=key[key.length-2];
while (j>=0) {
if (compare(myObj,v,j)) { //check to see if it is in the next lower array, if yes, check the next one.
j--;
}
if (j>0 && (compare(myObj,v,j+1))===true) lcm = v; //before the loop exits, set lcm = v
}
});
return lcm;
}

I would not use forEach since there is no way to exit from the method when you find the first match/failure. You would need to keep looping. Instead you should look into a regular for loop with every and indexOf. This code also assumes that the array is sorted so smallest number comes first. If not, a simple sort() with a clone of the array can solve that.
//pass in arrays, assumes array is sorted
function getFirstCommon (arrs) {
//Loop over the elements in the first array
for (var i=0; i<arrs[0].length; i++) {
//get the value for the current index
var val = arrs[0][i];
//make sure every array has the value
//if every does not find it, it returns false
var test = arrs.every( function (arr) {
//check the array to see if it has the element
return arr.indexOf(val)!==-1;
});
//If we find it, than return the current value
if (test) {
return val;
}
}
//if nothing was found, return null
return null;
}
//test numbers
var nums = [
[1,2,3,4,5,6],
[2,3,4,5,6],
[3,4,5,6],
[4,5,6,7,8,9],
[6,7,8,9]
];
console.log(getFirstCommon(nums)); //6
var nums2 = [
[1,2,3,4,5,6],
[2,3,4,5,6],
[3,4,5,6],
[4,5,6,7,8,9],
[5,7,8,9]
];
console.log(getFirstCommon(nums2)); //5
var nums3 = [
[1,2,3,4,5,6],
[7,8,9,10,11,12],
[7,8,9,10,11,12]
];
console.log(getFirstCommon(nums3)); //null
The code could be improved where it does not check itself

First of all, you do have an infinite loop. If first compare() fails, you never decrease j, and keep checking the same number.
Second: you key array never decreases, so you always compare two last arrays.

Related

How to check if an element is present in all of the subarrays passed into the function? JS

function intersection(...bigArr)
{
let results = [];
let compiledArr = [];
for(let i = 0; i < bigArr.length; i++) {//bigArr.length is the number of subarrays
compiledArr.push(...bigArr[i]);//takes each subarray, deconstructs it and the elements are pushed into compiledArr.
}
const frequencyObj = {};
let endBound = bigArr[0].length;
let k = 1
for(let i = 0; i < compiledArr.length; i++)
{
//if the element exists, increase its frequency by 1. If it doesn't, create it and initialize it to 1. After, check if its frequency value === bigArr.length. If so, push to results
let currentElement = compiledArr[i];
if(i === endBound)//the program has reached the next subarray
{
endBound += bigArr[k].length;
k++;
turnTrue(frequencyObj) //turn the boolean value for each property to true. This is because we are analyzing a different subarray.
}
if(!frequencyObj[compiledArr[i]])//if the element DNE in the object, then add it
frequencyObj[compiledArr[i]] = [1, false];
else if( frequencyObj[currentElement] && frequencyObj[currentElement][1] === true)//if the element already exists, we need to make sure that we only increment its frequency iff we are in a different subarray within compiledArr
{
frequencyObj[currentElement][0] += 1;
frequencyObj[currentElement][1] = false;
//check if the frequency of that element === bigArr.length, it means it appears in all subarrays.
if(frequencyObj[currentElement][0] === bigArr.length)
results.push(currentElement);
}
}
return results;
}
function turnTrue(obj)
{
for(let key in obj)
obj[key][1] = true;
}
let result = intersection([1,2,1], [4,1,3,1,4], [3, 1, 2,6]);
console.log(result);
The program above has the the purpose of outputting an array with elements that are present in all of the subarrays passed into the intersection function.
This program accounts for duplicates within one subarray. The frequency is only marked when an element appears for the first time in a subarray. For example, given the three test subarrays [1,2,1], [4,1,3,1,4], [3, 1, 2,6], any element can only have a max frequency of the number of subarrays(in this case 3). An element is only marked once per subarray.
This is the only solution I could think of and I know that there exists a more simpler solution. Can anyone rate this code and indicate what could be a better solution?
It sounds like you're asking for a basic Set intersection, which can be done using the standard JavaScript Set object. Please see Set for more info.
You could however be asking for a count of how many times each element appears, but your question isn't explicit about this (and can only be either 0 if the set is empty, or the .length of your data array)
data = [ [1,2,1], [4,1,3,1,4], [3, 1, 2,6] ]
// intersection is taken verbatim from MDN docs linked
function intersection(setA, setB) {
const _intersection = new Set();
for (const elem of setB) {
if (setA.has(elem)) {
_intersection.add(elem);
}
}
return _intersection;
}
let set = data.reduce( (a, e) => {
// test includes the possibility that the leading elements are empty
return (a && a.size)? intersection(a, new Set(e)) : new Set(e)
}, null)
console.log([...set])

Question about recursive JS - from freecodecamp

There is this function:
function countup(n) {
if (n < 1) {
return [];
} else {
const countArray = countup(n - 1);
countArray.push(n);
return countArray;
}
}
console.log(countup(5));
How is [] being pushed first? From the code, it looks like it is being counted down from n. Can someone explain?
Bonus question: Why write like this when I can write:
function countup(n){
let answer = []
for (let i = 1; i <= n; i++){
(i<n) ? answer.push(i)
: answer.push(n)
}
return answer
}
console.log(countup(5));
which is a shorter function. thank you in advance!
Even though the numbers are in descending order starting from n, they are actually being pushed smallest number first. This is because before the push() operation can happen we keep going down in the recursion stack when we call countup(n - 1) every layer of the recursion. This is why we reach a point where n < 1 i.e. 0 where an empty array is returned.
Next, we encounter the first array.push() operation and so the first number that is pushed into the array is actually 1 and not n. Next, the recursion layers start winding up and we push numbers 2, 3, and so on till n.
Yes you can achieve the same using a traditional for loop, but I guess they just want to explain how recursion works.
To understand recursions, sometimes you need to follow the execution of the code manually. Now the following line causes the recursive call and subtracts one from the number and then passes it to the recursive call.
const countArray = countup(n - 1);
Now imagine, if you passed 5 to the initial function call like so:
countup(5);
As 5 is not less than 1, the else block will be executed. The first line in the else block is the recursive call. So when your program encounters the recursive call, it stops execution of the current function until that recursive function call is concluded/returned. So the first recursive call will be:
const countArray = countup(4);
consequently, the same process will be repeated until n equals 0. As 0 is smaller than 1, the if condition will evaluate to true. and it will return an empty array.
After the recursive call, it pushes the number to the array returned by recursive call. so the array at that point will contain [0] and then
[0, 1] and it will keep adding numbers till the all recursive calls have been computed and then it will execute the rest of the intial function call.
Often times, recursion can be replaced by loops but thats not the case always. As a programmer one should be aware of this important topic.
Since countup() is fully divided by an if statement that depends on n, let's first see what exectutes for different vales of n.
To make it a lot easier to follow, we'll append our current n to countArray's variable name:
n == 5:
const countArray5 = countup(4);
countArray5.push(5);
return countArray5;
⋮
n == 1:
const countArray1 = countup(0);
countArray1.push(1);
return countArray1;
n == 0:
return [];
Now let's sequentially expand each inner call to countup(n):
countup(5) {
const countArray5 = countup(4);
countArray5.push(5);
return countArray5;
}
countup(5) {
const countArray5 = countup(4) {
const countArray4 = countup(3);
countArray4.push(4);
return countArray4;
};
countArray5.push(5);
return countArray5;
}
Expanding until countup(0):
countup(5) {
const countArray5 = countup(4) {
const countArray4 = countup(3) {
const countArray3 = countup(2) {
const countArray2 = countup(1) {
const countArray1 = countup(0) // countup(0) returns [] so countArray1 = [];
countArray1.push(1); // Then push '1' to countArray1;
return countArray1; // Return [1] at counterArray2 = countup(1) above
};
countArray2.push(2); // countArray2 was [1], now push '2' to make it [1,2]
return countArray2; // Return [1,2] at countArray3 = countup(2) above
};
countArray3.push(3); // Push '3' to countArray3 => [1,2,3]
return countArray3; // return [1,2,3] at countArray4 = countup(3)
};
countArray4.push(4); // Push '4' to countArray4 => [1,2,3,4]
return countArray4; // Return [1,2,3,4] at countArray5 = countup(4)
};
countArray5.push(5); // Push '5' to countArray5 => [1,2,3,4,5]
return countArray5; // countup(5) returns [1,2,3,4,5]
}

Nodejs code is not executing as expected

I am trying the below line of codes. I want to save the numbers after each iteration of outer while loop in an array named sn. But, after each iteration sn contains only the numbers of last iteration. May be I am missing whole concept of sync and async.
function test() {
var numbers = [0, 2, 7, 0];
var check = true;
var sn = [];
var p = 0;
while (check) {
var index = numbers.indexOf(Math.max(...numbers));
var value = Math.max(...numbers);
numbers[index] = 0;
for (var i = value; i > 0; i--) {
var temp = ++index;
index = temp % (numbers.length);
numbers[index] += 1;
}
console.log("numbers", numbers);
if (sn.includes(numbers)) { check = false };
sn.push(numbers);
console.log("hey there=========");
}
}
test();
There is nothing to do with sync or async here.
Here what is happening is that, you are trying to push 'numbers' array to 'sn' array.
Statement is "sn.push(numbers);"
So here we are pushing the Object reference of numbers array to 'sn', means you are not making a copy of numbers array and pushing to 'sn'.
You are just pushing the Memory reference of 'numbers' array.
So during first iteration, 'sn' will have exact value as you calculates.
But during the second iteration 'sn' will have two arrays. But those two values are same and points to the same memory location of 'number'.
So here what you should do is create a clone of 'numbers' array during each iteration.
if (sn.includes(numbers)) { check = false };
var cloneArray = numbers.slice(0);
sn.push(cloneArray);
This if statement: if (sn.includes(numbers)) { check = false }; will never be true because the Array.prototype.includes() method does not accept an array as a parameter; only individual elements. numbers is an array and thus will never be truthy.
If you are trying to see if an array contains a sub-array. The answer that Mild Fuzz has in this stack overflow: Javascript array contains/includes sub array should work.

Compare two arrays and create a new array with the differences, accounting for duplicates

Lets say I have two arrays containing numbers..
[1,1,7,6],
[1,7]
I need to create a new array out of the numbers that do not appear in the second array, but also account for there being two 1s.
I have tried using lodash's _.difference([1,1,7,6],[1,7])
as well as some other plain Javascript functions, but they do not account for the there being two 1s, so I just end up with [6]
My desired outcome would be [1,6]
Order does not matter
Iterate over the first array. For each element in the first array, search the second. If it exists, insert null into that index. If it doesn't exist, then push it to a new array with your differences
const difference = (arr1, arr2) => {
const differenceArr = []
for (let i = 0; i < arr1.length; i++) {
const matchingIdx = arr2.indexOf(arr1[i]);
if (matchingIdx !== -1) {
arr2[matchingIdx] = null
} else {
differenceArr.push(arr1[i])
}
}
return differenceArr
}
console.log(difference([1, 1, 7, 7, 6],[1, 1, 7]))
A shorter script:
function differenceWithDuplicates(a, b) {
while(b.length) a.splice(a.indexOf(b[0]), +a.includes(b.shift()));
return a;
}
console.log(differenceWithDuplicates([1, 1, 7, 6],[1, 7]));
Or just make the while, without call it inside a function.
You could do something along the lines of:
const diff = (array1, array2) => {
const _array2 = Array.from(array2);
const difference = [];
array1.forEach((array1Item) => {
const matchedIndex = _array2.findIndex(array2Item => array2Item === array1Item);
if (matchedIndex > -1) {
// Remove from second array
_array2.splice(matchedIndex, 1);
} else {
// No match found, add to difference array
difference.push(array1Item);
}
});
// Return the remaining items in _array2 and difference
return [ ..._array2, ...difference ];
}
By copying the second array, you can remove any matches from it. And by combining that with matching array1 > array2, you get differences in both directions, including duplicates.
Working fiddle: https://jsfiddle.net/3v34tjnq/

Javascript array sort and unique

I have a JavaScript array like this:
var myData=['237','124','255','124','366','255'];
I need the array elements to be unique and sorted:
myData[0]='124';
myData[1]='237';
myData[2]='255';
myData[3]='366';
Even though the members of array look like integers, they're not integers, since I have already converted each to be string:
var myData[0]=num.toString();
//...and so on.
Is there any way to do all of these tasks in JavaScript?
This is actually very simple. It is much easier to find unique values, if the values are sorted first:
function sort_unique(arr) {
if (arr.length === 0) return arr;
arr = arr.sort(function (a, b) { return a*1 - b*1; });
var ret = [arr[0]];
for (var i = 1; i < arr.length; i++) { //Start loop at 1: arr[0] can never be a duplicate
if (arr[i-1] !== arr[i]) {
ret.push(arr[i]);
}
}
return ret;
}
console.log(sort_unique(['237','124','255','124','366','255']));
//["124", "237", "255", "366"]
You can now achieve the result in just one line of code.
Using new Set to reduce the array to unique set of values.
Apply the sort method after to order the string values.
var myData=['237','124','255','124','366','255']
var uniqueAndSorted = [...new Set(myData)].sort()
UPDATED for newer methods introduced in JavaScript since time of question.
This might be adequate in circumstances where you can't define the function in advance (like in a bookmarklet):
myData.sort().filter(function(el,i,a){return i===a.indexOf(el)})
Here's my (more modern) approach using Array.protoype.reduce():
[2, 1, 2, 3].reduce((a, x) => a.includes(x) ? a : [...a, x], []).sort()
// returns [1, 2, 3]
Edit: More performant version as pointed out in the comments:
arr.sort().filter((x, i, a) => !i || x != a[i-1])
function sort_unique(arr) {
return arr.sort().filter(function(el,i,a) {
return (i==a.indexOf(el));
});
}
How about:
array.sort().filter(function(elem, index, arr) {
return index == arr.length - 1 || arr[index + 1] != elem
})
This is similar to #loostro answer but instead of using indexOf which will reiterate the array for each element to verify that is the first found, it just checks that the next element is different than the current.
Try using an external library like underscore
var f = _.compose(_.uniq, function(array) {
return _.sortBy(array, _.identity);
});
var sortedUnique = f(array);
This relies on _.compose, _.uniq, _.sortBy, _.identity
See live example
What is it doing?
We want a function that takes an array and then returns a sorted array with the non-unique entries removed. This function needs to do two things, sorting and making the array unique.
This is a good job for composition, so we compose the unique & sort function together. _.uniq can just be applied on the array with one argument so it's just passed to _.compose
the _.sortBy function needs a sorting conditional functional. it expects a function that returns a value and the array will be sorted on that value. Since the value that we are ordering it by is the value in the array we can just pass the _.identity function.
We now have a composition of a function that (takes an array and returns a unique array) and a function that (takes an array and returns a sorted array, sorted by their values).
We simply apply the composition on the array and we have our uniquely sorted array.
This function doesn't fail for more than two duplicates values:
function unique(arr) {
var a = [];
var l = arr.length;
for(var i=0; i<l; i++) {
for(var j=i+1; j<l; j++) {
// If a[i] is found later in the array
if (arr[i] === arr[j])
j = ++i;
}
a.push(arr[i]);
}
return a;
};
Here is a simple one liner with O(N), no complicated loops necessary.
> Object.keys(['a', 'b', 'a'].reduce((l, r) => l[r] = l, {})).sort()
[ 'a', 'b' ]
Explanation
Original data set, assume its coming in from an external function
const data = ['a', 'b', 'a']
We want to group all the values onto an object as keys as the method of deduplication. So we use reduce with an object as the default value:
[].reduce(fn, {})
The next step is to create a reduce function which will put the values in the array onto the object. The end result is an object with a unique set of keys.
const reduced = data.reduce((l, r) => l[r] = l, {})
We set l[r] = l because in javascript the value of the assignment expression is returned when an assignment statement is used as an expression. l is the accumulator object and r is the key value. You can also use Object.assign(l, { [r]: (l[r] || 0) + 1 }) or something similar to get the count of each value if that was important to you.
Next we want to get the keys of that object
const keys = Object.keys(reduced)
Then simply use the built-in sort
console.log(keys.sort())
Which is the set of unique values of the original array, sorted
['a', 'b']
The solution in a more elegant way.
var myData=['237','124','255','124','366','255'];
console.log(Array.from(new Set(myData)).sort((a,b) => a - b));
I know the question is very old, but maybe someone will come in handy
A way to use a custom sort function
//func has to return 0 in the case in which they are equal
sort_unique = function(arr,func) {
func = func || function (a, b) {
return a*1 - b*1;
};
arr = arr.sort(func);
var ret = [arr[0]];
for (var i = 1; i < arr.length; i++) {
if (func(arr[i-1],arr[i]) != 0)
ret.push(arr[i]);
}
}
return ret;
}
Example: desc order for an array of objects
MyArray = sort_unique(MyArray , function(a,b){
return b.iterator_internal*1 - a.iterator_internal*1;
});
No redundant "return" array, no ECMA5 built-ins (I'm pretty sure!) and simple to read.
function removeDuplicates(target_array) {
target_array.sort();
var i = 0;
while(i < target_array.length) {
if(target_array[i] === target_array[i+1]) {
target_array.splice(i+1,1);
}
else {
i += 1;
}
}
return target_array;
}
I guess I'll post this answer for some variety. This technique for purging duplicates is something I picked up on for a project in Flash I'm currently working on about a month or so ago.
What you do is make an object and fill it with both a key and a value utilizing each array item. Since duplicate keys are discarded, duplicates are removed.
var nums = [1, 1, 2, 3, 3, 4, 5, 5, 6, 7, 7, 8, 9, 9, 10];
var newNums = purgeArray(nums);
function purgeArray(ar)
{
var obj = {};
var temp = [];
for(var i=0;i<ar.length;i++)
{
obj[ar[i]] = ar[i];
}
for (var item in obj)
{
temp.push(obj[item]);
}
return temp;
}
There's already 5 other answers, so I don't see a need to post a sorting function.
// Another way, that does not rearrange the original Array
// and spends a little less time handling duplicates.
function uniqueSort(arr, sortby){
var A1= arr.slice();
A1= typeof sortby== 'function'? A1.sort(sortby): A1.sort();
var last= A1.shift(), next, A2= [last];
while(A1.length){
next= A1.shift();
while(next=== last) next= A1.shift();
if(next!=undefined){
A2[A2.length]= next;
last= next;
}
}
return A2;
}
var myData= ['237','124','255','124','366','255','100','1000'];
uniqueSort(myData,function(a,b){return a-b})
// the ordinary sort() returns the same array as the number sort here,
// but some strings of digits do not sort so nicely numerical.
function sort() only is only good if your number has same digit, example:
var myData = ["3","11","1","2"]
will return;
var myData = ["1","11","2","3"]
and here improvement for function from mrmonkington
myData.sort().sort(function(a,b){return a - b;}).filter(function(el,i,a){if(i==a.indexOf(el) & el.length>0)return 1;return 0;})
the above function will also delete empty array and you can checkout the demo below
http://jsbin.com/ahojip/2/edit
O[N^2] solutions are bad, especially when the data is already sorted, there is no need to do two nested loops for removing duplicates. One loop and comparing to the previous element will work great.
A simple solution with O[] of sort() would suffice. My solution is:
function sortUnique(arr, compareFunction) {
let sorted = arr.sort(compareFunction);
let result = sorted.filter(compareFunction
? function(val, i, a) { return (i == 0 || compareFunction(a[i-1], val) != 0); }
: function(val, i, a) { return (i == 0 || a[i-1] !== val); }
);
return result;
}
BTW, can do something like this to have Array.sortUnique() method:
Array.prototype.sortUnique = function(compareFunction) {return sortUnique(this, compareFunction); }
Furthermore, sort() could be modified to remove second element if compare() function returns 0 (equal elements), though that code can become messy (need to revise loop boundaries in the flight). Besides, I stay away from making my own sort() functions in interpreted languages, since it will most certainly degrade the performance. So this addition is for the ECMA 2019+ consideration.
The fastest and simpleness way to do this task.
const N = Math.pow(8, 8)
let data = Array.from({length: N}, () => Math.floor(Math.random() * N))
let newData = {}
let len = data.length
// the magic
while (len--) {
newData[data[len]] = true
}
var array = [2,5,4,2,5,9,4,2,6,9,0,5,4,7,8];
var unique_array = [...new Set(array)]; // [ 2, 5, 4, 9, 6, 0, 7, 8 ]
var uniqueWithSorted = unique_array.sort();
console.log(uniqueWithSorted);
output = [ 0, 2, 4, 5, 6, 7, 8, 9 ]
Here, we used only Set for removing duplicity from the array and then used sort for sorting array in ascending order.
I'm afraid you can't combine these functions, ie. you gotta do something like this:-
myData.unique().sort();
Alternatively you can implement a kind of sortedset (as available in other languages) - which carries both the notion of sorting and removing duplicates, as you require.
Hope this helps.
References:-
Array.sort
Array.unique

Categories

Resources