Compare arrays of objects independent of the order - javascript

I have 2 arrays of objects and I have to compare them, but the order of the objects DOES NOT matter. I can't sort them because I won't have their keys' names because the functions must be generic. The only information that I'll have about the array is that both array's objects have the same amount of keys and those keys have the same name. So the array1 must contain the same objects as the array2.
var array1 = [{"key1":"Banana", "key2":"Yammy"}, {"key1":"Broccoli", "key2":"Ew"}];
var array2 = [{"key1":"Broccoli", "key2":"Ew"}, {"key1":"Banana", "key2":"Yammy"}];
In the example, array1 must be equal array2.
I tryed to use the chai .eql() method but it didn't work.

The following solution:
will verify that the arrays have an equal number of elements
does not impose restrictions on keys (as to not contain a certain delimiter)
requires both keys and (string) values to be the same
has a time complexity of O(nlogn) (instead of O(n²) as some other solutions here)
function equalArrays(a, b) {
if (a.length !== b.length) return false;
const ser = o => JSON.stringify(Object.keys(o).sort().map( k => [k, o[k]] ));
a = new Set(a.map(ser));
return b.every( o => a.has(ser(o)) );
}
// Example
var array1 = [{"key1":"Banana", "key2":"Yammy"}, {"key1":"Broccoli", "key2":"Ew"}];
var array2 = [{"key1":"Broccoli", "key2":"Ew"}, {"key1":"Banana", "key2":"Yammy"}];
console.log(equalArrays(array1, array2)); // true
// Example with different key name
var array1 = [{"key0":"Banana", "key2":"Yammy"}, {"key1":"Broccoli", "key2":"Ew"}];
var array2 = [{"key1":"Broccoli", "key2":"Ew"}, {"key1":"Banana", "key2":"Yammy"}];
console.log(equalArrays(array1, array2)); // false

You can array#join each value of the object on an separator and then generate a new array of string and then compare each values using array#every and array#includes
var array1 = [{"key1":"Banana", "key2":"Yammy"}, {"key1":"Broccoli", "key2":"Ew"}];
array2 = [{"key1":"Broccoli", "key2":"Ew"}, {"key1":"Banana", "key2":"Yammy"}];
values = (o) => Object.keys(o).sort().map(k => o[k]).join('|'),
mapped1 = array1.map(o => values(o)),
mapped2 = array2.map(o => values(o));
var res = mapped1.every(v => mapped2.includes(v));
console.log(res);

You can do something like following:
For each object in each array you can calc its representation:
arr1.forEach( (obj) => {
obj.representation = '';
for (let key of Object.keys(obj)) {
obj.representation += obj[key];
}
}
Same for arr2
now you can sort both arrays by representation for example and then compare.
To sort do the following:
arr1.sort( (a,b) => { return a.representation > b.representation } );
arr2.sort( (a,b) => { return a.representation > b.representation } );
After sorting you can compare both arrays
let equal = arr1.every( (el, i) => arr2[i]===el );

Related

Cluster two object arrays using months

I have two sets of arrays, one containing date values in months(in no order) the other containing corresponding month values as such;
array1=['jan','mar','dec','jan','sep','nov','mar']
array2=[3,5,5,4,5,8,2]
as seen, each month can appear more than once.
id like to cluster/sort this data to have 2 arrays that show month and corresponding total values, in essence, get all the values that correspond to january, sum them and output them to another array as well as a forth array containing months, without repeats. something like
array3=['jan','mar','sep',nov','dec']
array4=[7,7,5,5,8] //totals
you can use a dictionary to keep track of identical months. If months repeated it will add to the value in dictionary.
array1=['jan','mar','dec','jan','sep','nov','mar']
array2=[3,5,5,4,5,8,2]
res = {}
for(let i=0;i<array1.length;i++){
if(array1[i] in res){
res[array1[i]]+= array2[i]
} else {
res[array1[i]] = array2[i]
}
}
array3 = []
array4 = []
for(let i in res){
array3.push(i)
array4.push(res[i])
}
You could use new Map() here and then just map to pick necessary values.
array1=['jan','mar','dec','jan','sep','nov','mar']
array2=[3,5,5,4,5,8,2]
const months = new Map();
array1.forEach((month, index) => {
months.set(month, (months.get(month) || 0) + array2[index]);
});
const array3 = Array.from(months, ([month]) => month);
const array4 = Array.from(months, ([month, summ]) => summ);
console.log(array3);
console.log(array4);
This approach can also sort based on month number.
let array1=['jan','mar','dec','jan','sep','nov','mar']
let array2=[3,5,5,4,5,8,2]
//this is for saving the index and final sorting after monthwise summing up values
monthsObj = {};
['jan','feb','mar','apr','may','jun','jul','aug','sep','oct','nov','dec'].forEach((name,index) => {monthsObj[name]=index})
function getSum(a1, a2){
let obj = {};
for(let i=0;i<a1.length;i++){
//if the month is not present in the obj
if(obj[a1[i]] === undefined) obj[a1[i]] = a2[i];
else obj[a1[i]] += a2[i]; //otherwise add to the previous month value
}
//sort based on month numbers
let sorted = [...Object.entries(obj)].sort(function(a, b) {
return monthsObj[a[0]]-monthsObj[b[0]];
});
let final1 = [], final2=[];
for(let i=0;i<sorted.length;i++){
final1.push(sorted[i][0]);
final2.push(sorted[i][1]);
}
console.log(final1, final2);
}
getSum(array1, array2);
The Set object lets you store unique values of any type, whether primitive values or object references. With that, you can omit duplicate values in your array. (you can read this resource for more information about Set.)
The Object.values() method returns an array of a given object's own enumerable property values, in the same order as that provided by a for...in loop. (The only difference is that a for...in loop enumerates properties in the prototype chain as well.) for more details, you can read this
array1=['jan','mar','dec','jan','sep','nov','mar']
array2=[3,5,5,4,5,8,2]
const months = {};
array1.forEach((month, index) => {
months[month]=(months[month] || 0) + array2[index];
});
const array3 = Array.from(new Set(array1));
const array4 = Object.values(months) ;
console.log(array3);
console.log(array4);

Find Unique value from an array based on the array's string value (Javascript)

so I want to find unique values from an array.
so for example I have this array:
const mainArr = ['shape-10983', 'size-2364', 'size-7800', 'size-4602', 'shape-11073', 'size-15027', 'size-15030', 'size-15033', 'height-3399', 'height-5884']
so I want to find the first matching value for each unique item.
for example, in the array, I have two strings with the shape prefix, six items with the size prefix, and two items with the height prefix.
so I want to output to be something like
const requiredVal = ["shape-10983", "size-2364", "height-3399"]
I want only the first value from any set of different values.
the simplest solution will be to iterate on the list and storing what you got in a dictionary
function removeSimilars(input) {
let values = {};
for (let value of input) {//iterate on the array
let key = value.splitOnLast('-')[0];//get the prefix
if (!(key in values))//if we haven't encounter the prefix yet
values[key] = value;//store that the first encounter with the prefix is with 'value'
}
return Object.values(values);//return all the values of the map 'values'
}
a shorter version will be this:
function removeSimilars(input) {
let values = {};
for (let value of input)
values[value.splitOnLast('-')[0]] ??= value;
return Object.values(values);
}
You could split the string and get the type and use it aks key for an object along with the original string as value. At result take only the values from the object.
const
data = ['shape-10983', 'size-2364', 'size-7800', 'size-4602', 'shape-11073', 'size-15027', 'size-15030', 'size-15033', 'height-3399', 'height-5884'],
result = Object.values(data.reduce((r, s) => {
const [type] = s.split('-', 1);
r[type] ??= s;
return r;
}, {}));
console.log(result);
If, as you mentioned in the comments, you have the list of prefixes already available, then all you have to do is iterate over those, to find each first element that starts with that prefix in your full list of possible values:
const prefixes = ['shape', 'size', 'height'];
const list = ['shape-10983', 'size-2364', 'size-7800', 'size-4602', 'shape-11073', 'size-15027', 'size-15030', 'size-15033', 'height-3399', 'height-5884']
function reduceTheOptions(list = [], prefixes = [], uniques = []) {
prefixes.forEach(prefix =>
uniques.push(
list.find(e => e.startsWith(prefix))
)
);
return uniques;
}
console.log(reduceTheOptions(list, prefixes));
Try this:
function getRandomSet(arr, ...prefix)
{
// the final values are load into the array result variable
result = [];
const randomItem = (array) => array[Math.floor(Math.random() * array.length)];
prefix.forEach((pre) => {
result.push(randomItem(arr.filter((par) => String(par).startsWith(pre))));
});
return result;
}
const mainArr = ['shape-10983', 'size-2364', 'size-7800', 'size-4602', 'shape-11073', 'size-15027', 'size-15030', 'size-15033', 'height-3399', 'height-5884'];
console.log("Random values: ", getRandomSet(mainArr, "shape", "size", "height"));
I modified the #ofek 's answer a bit. cuz for some reason the ??= is not working in react project.
function removeSimilars(input) {
let values = {};
for (let value of input)
if (!values[value.split("-")[0]]) {
values[value.split("-")[0]] = value;
}
return Object.values(values);
}
create a new array and loop over the first array and check the existing of element before in each iteration if not push it to the new array

How do I find a common element between two arrays in JavaScript?

If I have two arrays of strings in JavaScript, how do I extract the strings that both arrays have in common? For example, if I have one array ['Apple','Orange','Grape'] and another array ['Apple'], how do I get the result Apple?
This is a data structure problem.
You can simply create an object for Array1, and then search for the keys in Object which are in Array2.
Array1 = ['Apple','Orange','Grape'];
Array2 = ['Apple','Lemon','Mango','Grape'];
array1 = ['Apple','Orange','Grape'];
array2 = ['Apple','Lemon','Mango','Grape'];
let obj = {};
for (let fruit of array1) {
obj[fruit] = 1;
}
let commonArray = [];
array2.forEach(fruit => {
if (obj[fruit] === 1) {
commonArray.push(fruit);
}
})
common array will contain Apple and Grape;

How to group multiple sets of duplicate integers in an array into their own array of arrays?

I am trying to split an array of integers into an array of arrays by duplicate values. The original array is composed of a list of 6 digit integers, some of these integers come in pairs, others come in groups of 3 or 4s. I'd like to get these duplicates pushed to their own arrays and have all of these arrays of duplicates composed into an array of arrays that I can later loop through.
I've looked on in the lodash library for some method or combination of but can't quite find anything that seems to work. I've also tried a few different configurations with nested for loops but also am struggling with that.
const directory = "X/";
let files = fs.readdirSync(directory);
let first6Array = [ ];
for(i=0; i< files.length; i++){
let first6 = files[i].substring(0, 6);
first6Array.push(first6);
};
console.log(first6Array);
example output of first6Array:
[ '141848',
'141848',
'141848',
'142851',
'142851',
'143275',
'143275']
I'd like to end up with something like
let MasterArray = [[141848,141848,141848],[142851,142851],[143275,143275]];
You can use new Set() to filter out the duplicates.
Then you use the unique Array and filter for every value.
const firstArray = [ '141848', '141848', '141848', '142851', '142851', '143275', '143275'];
const numberArray = firstArray.map(Number);
const masterArray = [];
const unique = new Set (numberArray); // Set {141848, 142851, 143275}
unique.forEach(u => {
masterArray.push(numberArray.filter(e => e === u));
});
console.log(masterArray);
Using lodash, you can create a function with flow:
map the items by truncating them and converting to numbers.
groupBy the value (the default).
convert to an array of arrays using values.
const { flow, partialRight: pr, map, truncate, groupBy, values } = _;
const truncate6 = s => truncate(s, { length: 6, omission: '' });
const fn = flow(
pr(map, flow(truncate6, Number)),
groupBy,
values,
);
const firstArray = [ '141848abc', '141848efg', '141848hij', '142851klm', '142851opq', '143275rst', '143275uvw'];
const result = fn(firstArray);
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
Use reduce to create an object of arrays, indexed by number, and push to the associated array on each iteration (creating the array at the key first if needed), then get the values of the object:
const directory = "X/";
const files = fs.readdirSync(directory);
const output = Object.values(
files.reduce((a, file) => {
const num = Number(file.slice(0, 6));
if (!a[num]) a[num] = [];
a[num].push(num);
return a;
}, {})
);
It's pretty weird to have an array of identical values, though - you might consider a different data structure like
{
'141848': 3,
'142851': 2
}
to keep track of the number of occurrences of each number:
const output = files.reduce((a, file) => {
const num = file.slice(0, 6);
a[num] = (a[num] || 0) + 1;
return a;
}, {})
To obtain exactly the result you desire, you need a nested find, something like this should works:
const directory = "X/";
let files = fs.readdirSync(directory);
let first6Array = files.reduce((acc, value)=> {
let n = +value.substr(0, 6); // assumes it can't be NaN
let arr = acc.find(nested => nested.find(item => item === n));
if (arr) {
arr.push(n);
} else {
acc.push([n]);
}
return acc;
}, []);
console.log(first6Array);
Notice that an hashmap instead, with the value and the number of occurrence, would be better, also in term of performance, but I don't think it mind since you have really few elements.
Also, it assumes the first six characters are actually numbers, otherwise the conversion would fail and you'll get NaN.
It would be safer adding a check to skip this scenario:
let n = +value.substr(0, 6);
if (isNaN(n)) {
return acc;
}
// etc

How to create an array of unique keys with the largest element?

I am trying to find out largest number from groups and assign them into an array of unique groups. I tried using map and lodash uniq but didnt help
Example
Lets say the main array consists of a typeid and a number seperated by colon (:)
I want to identify the largest number in from each typeid and they create an array out of it.
From the below example i am trying to generate an output like below
["abcdwidets:6564","nightly:6543"]
6564 & 6543 are the largest values in their respective groups
Code
var mainarray = ["abcdwidets:1234","abcdwidets:3432","abcdwidets:6564","nightly:3423","nightly:6543"]
var arr = [];
var needle;
var i = 0;
var flag = 0;
mainarray.forEach( (element) => {
arr = element.split(":");
arr = arr.map((val) => {
return val
});
})
Reduce the array into a Map, and get the highest number for each key (the string before the :). Then Array.map() the Map's entries iterator (by spreading) back to an array:
const mainarray = ["abcdwidets:1234","abcdwidets:3432","abcdwidets:6564","nightly:3423","nightly:6543"];
const result = [...mainarray.reduce((r, s) => {
const [k, v] = s.split(':');
if(!r.has(k) || r.get(k) < v) r.set(k, v);
return r;
}, new Map())]
.map(([k, v]) => `${k}:${v}`);
console.log(result);
U will need to split the mainArray into 2 arrays like this
arr1=['abcdwidets', 'nightly', ..., n];
arr2=[1628, 0378, 6144, ..., n];
Once u have splitted your main array into these 2 then you just do a bubble sort and turn these 2 into 1 again.

Categories