How to compare two arrays using lodash (the order matters) - javascript

var arr1=[3,4,5,6,7,1,9];
var arr2=[1,3,4,6,7,5,9];
I want to compare arr2 to arr1. But the methods difference() and intersection() only seem to find if the two arrays have the same elements or not. I want to compare the two arrays spot by spot like arr1[0] to arr2[0], arr1[1] to arr2[1]. And it should show:
intersection: 6,7,9
difference: 1,3,4,5
How can I achieve this?

You can do this in lodash by zipping both arrays, filtering, and than taking the last item of each pair. The comperator for intersection is that the pair is equal. The comperator for difference is that the pair are not equal.
const arr1 = [3,4,5,6,7,1,9];
const arr2 = [1,3,4,6,7,5,9];
const compare = (comperator) => (arr1, arr2) =>
_.zip(arr1, arr2)
.filter(comperator)
.map(_.last);
const eq = _.spread(_.eq);
const intersection = compare(eq);
const difference = compare(_.negate(eq));
console.log('intersection ', intersection(arr1, arr2));
console.log('difference ', difference(arr1, arr2));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>

You could iterate both arrays in parallel and sort them into two seperate Sets:
function* parallel(a, b) {
for(let i = 0; i < a.length || i < b.length; i++)
yield [ a[i], b[i] ];
}
const intersection = new Set,
difference = new Set;
for(const [a, b] of parallel(arr1, arr2)) {
if(a === b)
intersection.add(a);
else
difference.add(a).add(b);
}
console.log([...intersection], [...difference]);

This also could be solved with reduce:
var arr1 = [3, 4, 5, 6, 7, 1, 9];
var arr2 = [1, 3, 4, 6, 7, 5, 9];
const f = (a, b) => b.reduce((r,c,i) => (a[i] == c ?
r.intersection.push(c) :
r.difference.push(c), r), {intersection: [], difference: []})
console.log(f(arr1, arr2))
Where you start with a pre-set accumulator object and compare each array value using the index.

You could use xor from lodash and it will return an empty array if the arrays have the same elements.
const a1= ['a', 'b', 'd']
const a2= ['d', 'b', 'a']
_.xor(a1, a2).length;//0

Why don't you write your own utility function that checks equality of the sequence? Something like:
export function sequenceEqual<T>(firstSequence: T[], secondSequence: T[]): boolean {
if(!firstSequence || !secondSequence) return false;
return firstSequence.every(
(d, i) => d === secondSequence[i]
);
}
This way you can just return boolean. There is no need to perform an extra step to check if the code returned some array or number or whatever, what is length of the returned type, which number it is etc. You just ask are my sequences equal and get true or false.
One more benefit is that you are not dependent on some library. Unless they have sequenceEqual so that you don't have to write from scratch, just call it, but I couldn't find it yet in Lodash.

Related

Find the min/max element of an Array containing strings in JavaScript

How can I obtain the max number of a JavaScript Array containing strings?
const array = ['a', 3, 4, 2] // should return 4
Here's my answer but I got NaN
function maxNum(arr) {
for(let i=0; i<arr.length; i++){
return Math.max.apply(Math, arr);
}
}
maxNum(array) //NaN
You could use filter and typeof to check for number only.
const array = ['a', 3, 4, 2] // should return 4
function myArrayMax(x) {
return Math.max(...x.filter(x => typeof x === 'number')); //result is 4
}
console.log(myArrayMax(array)) //4
Using Math.max.apply method
const array = ['a', 3, 4, 2] // should return 4
function myArrayMax(x) {
return Math.max.apply(null, x.filter(x => typeof x === 'number')); //result is 4
}
console.log(myArrayMax(array)) //4
If you wanna ignore strings and only take max from numbers. Math.max accepts numbers, not an array.
let array = ['a', 3, 4, 2] // should return 4
array = array.filter(a => !isNaN(Number(a)));
let max = Math.max(...array);
console.log(max);
I think more efficient could be using array.prototype.reduce:
var result = ['a', 3, 4, 2].reduce(
(a,b) => isNaN(a) ? b : (a>=b) ? a : b , 0) ;
console.log(result);
Because it only loops one time the array to get the highest number. The option of filtering and then Math.max will require 2 loops.
//as a note: `isNaN` have weird behaviour ..
MDN ref: link
const array = ['a', 3, 4, 2]
let result = Math.max(...(array.filter(el=>!isNaN(el))))
console.log(result)

Find second largest elements in array with duplicates in javascript

Am having array to find second largest elements including repeated value pls find below example.
const arr= [1,2,5,5,6]
expected result should be
[5,5]
I tried with map and math.max but i stuck up on logical issue.kindly help me
Below snippet could help you
const arr = [1, 2, 5, 5, 6]
const max = Math.max(...arr)
const newArr = arr.filter(element => element !== max)
const newMax = Math.max(...newArr)
const secondLargest = arr.filter(element => element === newMax)
console.log(secondLargest)
Here is a simpler approach, However it may not be the best approach in terms of performance for large data
const ar= [1,2,5,5,6]
secmax = Math.max(...ar.filter((n,i) => Math.max(...ar) !=n ))
res = ar.filter(n =>n == secmax)
console.log(res)
Using a Set to extract unique values shortens the code quite a bit
var arr = [1,5,2,5,4,8];
var uniqueValues = [...new Set(arr)].sort((a, b) => b-a);
var secondHighest = uniqueValues[1]; // 0 is max, 1 is second highest, etc.
var result = arr.filter(x => x === secondHighest);
Please keep in mind that there should be some due diligence in accessing the results (what happens if the code is fed with empty arrays, or arrays with a single repeated value? There are many cases not covered here)
You could group the values and sort the array of arrays and get the second array.
const
array = [1, 2, 5, 5, 6],
result = Object
.values(array.reduce((r, v) => (r[v] = [...(r[v] || []), v], r), {}))
.sort(([a], [b]) => b - a)
[1];
console.log(result);

Remove out nested array based on another array

I have this nested array
let arr = [['first', 'second'], ['third', 'fourth'], ['second', 'third']]
now I want to filter/remove based on exactly this array.
let filter = ['first', 'second']
and now my expected output should be:
[['third', 'fourth'], ['second', 'third']]
I only have this piece of code:
arr.filter(str => str.indexOf('second') === -1)
Which doesn't give the expected output, it also removed ['second', 'third'] because it filters whatever element that contains 'second'.. so they must be a better way or an improvement to the code.
If you care about ordering and need exact matches, you can write a simple arrays equal method and then filter out any equal arrays:
const arrEq = (a, b) => a.length === b.length && a.every((e, i) => b[i] === e);
const arr = [['first', 'second'], ['third', 'fourth'], ['second', 'third']];
const filter = ['first', 'second'];
console.log(arr.filter(e => !arrEq(e, filter)));
If you want the same elements but order doesn't matter:
const arrItemsEq = (a, b, cmp) => {
if (a.length !== b.length) {
return false;
}
a = a.slice().sort(cmp);
b = b.slice().sort(cmp);
return a.every((e, i) => e === b[i]);
};
const arr = [["a", "b"], ["b", "c"]];
const filter = ["b", "a"];
const strCmp = (x, y) => x.localeCompare(y);
console.log(arr.filter(e => !arrItemsEq(e, filter, strCmp)));
If you want to filter out arr elements if they don't include at least one of each filter element:
const arr = [["a", "b"], ["b", "c"]];
const filter = ["b", "a"];
console.log(arr.filter(x => !filter.every(y => x.includes(y))));
You need to test two arrays for equality. [There are many ways to do it] but once you pick one, you can simply remove any array that is equal to another. To avoid reimplementing the wheel, I'll use the LoDash _.isEqual for demonstration purposes:
let arr = [['first', 'second'], ['third', 'fourth']]
let filter = ['first', 'second']
let result = arr.filter(item => !_.isEqual(filter, item));
console.log(result);
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.15/lodash.min.js"></script>
The equality function can be swapped to any implementation that satisfies you. A very simple one is simply:
function isEqual(a, b) {
return JSON.stringify(a) === JSON.stringify(b);
}
but it's not guaranteed to be correct (e.g, isEqual([1, 2], ["1,2"]) //true) and it's going to be slow with large inputs. But it might still work, depending on circumstances.
You can use filter to check if the element doesn't include every string of the filter array.
let arr = [['first', 'second'], ['third', 'fourth'], ['second', 'third']]
let filterout = ['first', 'second']
let arr2 = arr.filter(x => ! filterout.every(y => x.includes(y)))
console.log(arr2)
But by using filter it basically creates a new array with fewer elements. Which is good enough for a small array.
If if the goal is to directly change the original array, then those elements can be spliced from that array.
let arr = [ ['first', 'second'], ['third', 'fourth'], ['second', 'third'], ['second', 'first'] ]
let filterout = ['first', 'second']
// getting the indexes of the element that need to be removed
let idxArr = []
arr.forEach( (x, idx) => { if(x.every(y => filterout.includes(y))) idxArr.push(idx)})
// removing the elements from the array
idxArr.sort((i,j)=>j-i).forEach(idx => {arr.splice(idx, 1)})
console.log(arr)

Javascript always run a function on the longest array, and use the shortest inside that function

The following function should always compare all the items in the longest array and see if they exist in the shorter one.
Now the following code works, but it's a bit verbose.
const findSimilar = (arr1, arr2) => {
const arrLongest = (arr1.length < arr2.length) ? arr2 : arr1;
const arrShortest = (arr1.length < arr2.length) ? arr1 : arr2;
return arrLongest.filter((arrLongestItem) => arrShortest.includes(arrLongestItem));
};
console.log(findSimilar([1,2,3,4,3], [1,2,3])); // (3) [1, 2, 3, 3]
console.log(findSimilar([1,2,3], [1,2,3,4,3])); // (4) [1, 2, 3, 3]
Is it necessary to have the two variables arrLongest and arrShortest? Or is there another way.
Another way to write it would be to use sort and destructing.
And reuse the existing parameters:
const findSimilar = (arr1, arr2) => {
[arr1, arr2] = [arr1, arr2].sort((a, b) => a.length - b.length)
return arr2.filter((arrLongestItem) => arr1.includes(arrLongestItem));
};
Or stay with arrShortest and arrLongest as it would be more verbose what the variable exactly contains.
const findSimilar = (arr1, arr2) => {
const [arrShortest, arrLongest] = [arr1, arr2].sort((a, b) => a.length - b.length)
return arrShortest.filter((arrLongestItem) => arrLongest.includes(arrLongestItem));
};
I would always preferre additional variables that are correctly named over reusing variable names.
The advantage of using sort is that it expresses what is done, and you reduce the possible errors as arr1, arr2 are used only once in the expression, instead of three times like in the ?:
And you for sure can combine it with new Set in the answer of CertainPerformance
You can use the conditional operator just once and then destructure into the two variables for the arrays:
const findSimilar = (arr1, arr2) => {
const [arrLongest, arrShortest] = (arr1.length < arr2.length) ? [arr2, arr1] : [arr1, arr2];
return arrLongest.filter((arrLongestItem) => arrShortest.includes(arrLongestItem));
};
console.log(findSimilar([1,2,3,4,3], [1,2,3])); // (3) [1, 2, 3, 3]
console.log(findSimilar([1,2,3], [1,2,3,4,3])); // (4) [1, 2, 3, 3]
To reduce the computational complexity from O(n^2) to O(n), use a Set instead, for the shortest array:
const findSimilar = (arr1, arr2) => {
const [arrLongest, arrShortest] = (arr1.length < arr2.length) ? [arr2, arr1] : [arr1, arr2];
const shortSet = new Set(arrShortest);
return arrLongest.filter(shortSet.has, shortSet);
};
console.log(findSimilar([1,2,3,4,3], [1,2,3])); // (3) [1, 2, 3, 3]
console.log(findSimilar([1,2,3], [1,2,3,4,3])); // (4) [1, 2, 3, 3]

Merging of two arrays, store unique elements, and sorting in jQuery

var Arr1 = [1,3,4,5,6];
var Arr2 = [4,5,6,8,9,10];
I am trying to do merge these two arrays and output coming is [1,3,4,5,6,4,5,6]
I have used $.merge(Arr1, Arr2); this piece to merge them. Using alert I can see the merged array like above.
Now my question is how can I get the following output:
[1,3,4,5,6,8,9,10]
i.e. the elements should be unique as well as sorted in the same manner I have mentioned.
Please help.
You can use Array.prototype.sort() to do a real numeric sort and use Array.prototype.filter() to only return the unique elements.
You can wrap it into a helper similar to this:
var concatArraysUniqueWithSort = function (thisArray, otherArray) {
var newArray = thisArray.concat(otherArray).sort(function (a, b) {
return a > b ? 1 : a < b ? -1 : 0;
});
return newArray.filter(function (item, index) {
return newArray.indexOf(item) === index;
});
};
Note that the custom sort function works with numeric elements only, so if you want to use it for strings or mix strings with numbers you have to update it off course to take those scenarios into account, though the rest should not change much.
Use it like this:
var arr1 = [1, 3, 4, 5, 6];
var arr2 = [4, 5, 6, 8, 9, 10];
var arrAll = concatArraysUniqueWithSort(arr1, arr2);
arrAll will now be [1, 3, 4, 5, 6, 8, 9, 10]
DEMO - concatenate 2 arrays, sort and remove duplicates
There is many ways of doing this I'm sure. This was just the most concise I could think off.
merge two or more arrays + remove duplicities + sort()
jQuery.unique([].concat.apply([],[[1,2,3,4],[1,2,3,4,5,6],[3,4,5,6,7,8]])).sort();
One line solution using just javascript.
var Arr1 = [1,3,4,5,6];
var Arr2 = [4,5,6,8,9,10];
const sortedUnion = [... new Set([...Arr1,... Arr2].sort((a,b)=> a-b))]
console.log(sortedUnion)
This looks like a job for Array.prototype.indexOf
var arr3 = arr1.slice(), // clone arr1 so no side-effects
i; // var i so it 's not global
for (i = 0; i < arr2.length; ++i) // loop over arr2
if (arr1.indexOf(arr2[i]) === -1) // see if item from arr2 is in arr1 or not
arr3.push(arr2[i]); // it's not, add it to arr3
arr3.sort(function (a, b) {return a - b;});
arr3; // [1, 3, 4, 5, 6, 8, 9, 10]
a = [1, 2, 3]
b = [2, 3, 4]
$.unique($.merge(a, b)).sort(function(a,b){return a-b}); -> [1, 2, 3, 4]
Update:
This is a bad idea, since the 'unique' function is not meant for use on numbers or strings.
However, if you must then the sort function needs to be told to use a new comparator since by default it sorts lexicographically.
Using underscore.js:
_.union([1, 2, 3], [101, 2, 1, 10], [2, 1]).sort(function(a,b){return a-b});
=> [1, 2, 3, 10, 101]
This example is taken directly from underscore.js, a popular JS library which complements jQuery
I did that as follows, where t1 and t2 are my two tables.
The first command put the values of the table t2 to the t1. The second command removes the duplicate values from the table.
$.merge(t1, t2);
$.unique(t1);
function sortUnique(matrix) {
if(matrix.length < 1 || matrix[0].length < 1) return [];
const result = [];
let temp, ele;
while(matrix.length > 0) {
temp = 0;
for(let j=0; j<matrix.length; j++) {
if(matrix[j][0] < matrix[temp][0]) temp = j;
}
if(result.length === 0 || matrix[temp][0] > result[result.length-1]) {
result.push(matrix[temp].splice(0,1)[0]);
} else {
matrix[temp].splice(0,1);
}
if(matrix[temp].length===0) matrix.splice(temp, 1);
}
return result;
}
console.log(sortUnique([[1,4,8], [2,4,9], [1,2,7]]))
Using JavaScript ES6 makes it easier and cleaner. Try this:
return [...Arr1, ...Arr2].filter((v,i,s) => s.indexOf(v) === i).sort((a,b)=> a - b);
and there you have it. You could build it in a function like:
function mergeUniqueSort(Arr1, Arr2){
return [...Arr1, ...Arr2].filter((v,i,s) => s.indexOf(v) === i).sort((a,b)=> a - b);
}
and that settles it. You can also break it down using ES6. Use a Spread Operator to combine arrays:
let combinedArrays = [...Arr1, ...Arr2]
then get the unique elements using the filter function:
let uniqueValues = combinedArrays.filter((value, index, self ) => self.indexOf(value) === index)
Lastly you now sort the uniqueValue object:
let sortAscending = uniqueValues.sort((a-b) => a - b) // 1, 2, 3, ....10
let sortDescending = uniqueValues.sort((b-a) => b - a) // 10, 9, 8, ....1
So you could use any part, just in case.

Categories

Resources