comparing array of objects and assigning similarity score - javascript

I am attempting to compare two array of object and assigning them a similarity score based of common items in the array.
I was able to compare the arrays but I am running into issue with using the same concept on array of objects.
let array1 = [{key1:['item1','item2','item3','item4']},{key2:['event3','event4']}];
let array2 = [{key1:['item1','item4','item2','item8']},{key2:['event4','event2']}];
let arrayA=['item1','item2','item3','item4'];
let arrayB=['item1','item4','item2','item8'];
function SimilarityPercentage(arrayA,arrayB){
let answer =arrayA.filter(function(item) {
return arrayB.indexOf(item) >= 0;
}).length
return answer/(Math.max(arrayA.length,arrayB.length))*100
}
console.log(SimilarityPercentage(arrayA,arrayB));// 75
Given array1 and array2 , I would like the result split out a similarity score, similar to the function above. I would like to use the rand index calculation : https://en.wikipedia.org/wiki/Rand_index#targetText=The%20Rand%20index%20or%20Rand,is%20the%20adjusted%20Rand%20index.

You could get the values and calculate the common score.
function similarityPercentage(arrayA, arrayB) {
return 100 * arrayA.filter(Set.prototype.has, new Set(arrayB)).length / Math.max(arrayA.length, arrayB.length);
}
function similarities(a, b) {
var parts = a.map((o, i) => similarityPercentage(Object.values(o)[0], Object.values(b[i])[0]));
return parts.reduce((a, b) => a + b, 0) / parts.length;
}
var array1 = [{ key1: ['item1', 'item2', 'item3', 'item4'] }, { key2: ['event3', 'event4'] }],
array2 = [{ key1: ['item1', 'item4', 'item2', 'item8'] }, { key2: ['event4', 'event2'] }],
arrayA = ['item1', 'item2', 'item3', 'item4'],
arrayB = ['item1', 'item4', 'item2', 'item8'];
console.log(similarityPercentage(arrayA, arrayB)); // 75
console.log(similarities(array1, array2)); // 62.5

You could do something like this:
var array1 = [val1,val2,val3];
var array2 = [val1,val4,val5];
var sim = [];
var simscore = 9;
if (array1.length > array2.length) {
for (var i = 0; i < array1.length; i++) {
if(array1[i] == array2[i]) {
sim.push(i);
simarr = array1;
simscore ++;
}
}
}else{
for (var i = 0; i < array2.length; i++) {
if(array1[i] == array2[i]) {
sim.push(i);
simarr = array2;
simscore ++;
}
}
}
console.log(sim);
console.log("Percent similar: ", simscore/simarr.length);
This will add the similar index to an array sim and increase the count of similar indexes by 1, always for the longer array, then print the percent of similarity.

First of all, your sample arrays are not well-structured, and your problem can be done more quickly if you restructure them.
Since you have not provided a formula for calculating the similarity between array1 and array2, I assume that every each of these arrays has equal length, and every item in them represents an object which has only one property (with the same name), and that property itself is an array. One obvious approach is to calculate every similarity score of the related child arrays of these two arrays to be calculated and then calculate the total similarity by averaging every key's similarity score.
Assumptions:
array1 and array2 have equal length
The nth element of array1 has only one property with the name keyFoo and the nth element of array2 also has only one property with the name keyFoo and keyFoo property of these two arrays are arrays themselves and must be compared to each other.
This quickly can be done using already provided SimilarityPercentage function:
function SimilarityPercentage2 (array1, array2) {
let similaritySum = 0;
for (let i = 0; i < array1.length; i++) {
const elem = array1[i];
const key = Object.keys(elem)[0];
similaritySum += SimilarityPercentage(elem[key], array2[i][key]);
}
return similaritySum / array1.length;
}
console.log(SimilarityPercentage2(array1, array2));
// output: 62.5

Related

Sorting occurrences of an array, high to low. Then return the top 4 highest occurrences [duplicate]

What is an elegant way to take a javascript array, order by the frequency of the values, and then filter for uniques?
So,
["apples", "oranges", "oranges", "oranges", "bananas", "bananas", "oranges"]
becomes
["oranges, "bananas", "apples"]
Compute the frequency of each item first.
{
apples: 1,
oranges: 4,
bananas: 2
}
Then create an array from this frequency object which will also remove the duplicates.
["apples", "oranges", "bananas"]
Now sort this array in descending order using the frequency map we created earlier.
function compareFrequency(a, b) {
return frequency[b] - frequency[a];
}
array.sort(compareFrequency);
Here's the entire source (using the newly introduced Array functions in ECMA 5) and combining the de-duplication and frequency map generation steps,
function sortByFrequency(array) {
var frequency = {};
array.forEach(function(value) { frequency[value] = 0; });
var uniques = array.filter(function(value) {
return ++frequency[value] == 1;
});
return uniques.sort(function(a, b) {
return frequency[b] - frequency[a];
});
}
Same as above using the regular array iteration.
function sortByFrequencyAndRemoveDuplicates(array) {
var frequency = {}, value;
// compute frequencies of each value
for(var i = 0; i < array.length; i++) {
value = array[i];
if(value in frequency) {
frequency[value]++;
}
else {
frequency[value] = 1;
}
}
// make array from the frequency object to de-duplicate
var uniques = [];
for(value in frequency) {
uniques.push(value);
}
// sort the uniques array in descending order by frequency
function compareFrequency(a, b) {
return frequency[b] - frequency[a];
}
return uniques.sort(compareFrequency);
}
// returns most frequent to least frequent
Array.prototype.byCount= function(){
var itm, a= [], L= this.length, o= {};
for(var i= 0; i<L; i++){
itm= this[i];
if(!itm) continue;
if(o[itm]== undefined) o[itm]= 1;
else ++o[itm];
}
for(var p in o) a[a.length]= p;
return a.sort(function(a, b){
return o[b]-o[a];
});
}
//test
var A= ["apples","oranges","oranges","oranges","bananas","bananas","oranges"];
A.byCount()
/* returned value: (Array)
oranges,bananas,apples
*/
I was actually working on this at the same time - the solution I came up with is pretty much identical to Anurag's.
However I thought it might be worth sharing as I had a slightly different way of calculating the frequency of occurrences, using the ternary operator and checking if the value has been counted yet in a slightly different way.
function sortByFrequencyAndFilter(myArray)
{
var newArray = [];
var freq = {};
//Count Frequency of Occurances
var i=myArray.length-1;
for (var i;i>-1;i--)
{
var value = myArray[i];
freq[value]==null?freq[value]=1:freq[value]++;
}
//Create Array of Filtered Values
for (var value in freq)
{
newArray.push(value);
}
//Define Sort Function and Return Sorted Results
function compareFreq(a,b)
{
return freq[b]-freq[a];
}
return newArray.sort(compareFreq);
}
Basic strategy:
Create an object to use as a hash table to track the frequency of each item in the array to be sorted.
Create a new array containing the item, frequency pairs.
Sort this array on frequency in descending order.
Extract the items from that array.
Code:
function descendingUniqueSort(toBeSorted) {
var hash = new Object();
toBeSorted.forEach(function (element, index, array) {
if (hash[element] == undefined) {
hash[element] = 1;
}
else {
hash[element] +=1;
}});
var itemCounts = new Array();
for (var key in hash) {
var itemCount = new Object();
itemCount.key = key;
itemCount.count = hash[key];
itemCounts.push(itemCount);
}
itemCounts.sort(function(a,b) { if(a.count<b.count) return 1;
else if (a.count>b.count) return -1; else return 0;});
return itemCounts.map(function(itemCount) { return itemCount.key; });
}
var arr = ["apples", "oranges", "oranges", "oranges", "bananas", "bananas", "oranges"].sort();
var freq = {};
for (var s in arr) freq[s] = freq[s] ? freq[s] + 1 : 0;
arr.sort(function(a, b) { return freq[a] > freq[b] ? -1 : 1; });
for (var i = arr.length - 1; i > 0; i--) if (arr[i] == arr[i - 1]) arr.splice(i,1);
alert(arr.join(","));
for the first step to compute
{
oranges: 4,
bananas: 2,
apples: 1
}
you can use countBy function of underscroe.js
var all=["apples", "oranges", "oranges", "oranges", "bananas", "bananas", "oranges"];
var frequency=_.countBy(all,function(each){return each});
so frequency object will contain frequency of all unique values, and you can get an unique list by simply calling _.uniq(all), and to sort that unique list by the _.sortBy method of underscore and using your frequency object you can use
_.sortBy(_.uniq(all),function(frequencyKey){return -frequency[frequencyKey]});
-ve sign is used here to sort the list in decending order by means of frequency value as per your requirement.
You can check the the documentation of http://underscorejs.org/ for further optimization by your own trick :)
Let me put a minimal code to get unique values (and with frequencies) in ES6.
var arr = ["apples", "oranges", "oranges", "oranges", "bananas", "bananas", "oranges"];
console.log([...new Set(arr)])
It is also applied to array of objects to aggregate some properties.
var arr = [{"fruit":"apples"}, {"fruit":"oranges"}, {"fruit":"oranges"}, {"fruit":"oranges"}, {"fruit":"bananas"}, {"fruit":"bananas"}, {"fruit":"oranges"}];
console.log(arr.reduce((x,y)=>{if(x[y.fruit]) {x[y.fruit]++;return x;} else {var z={};z[y.fruit]=1;return Object.assign(x,z);}},{}))
Create a counter of the array's elements using reduce:
arr.reduce(
(counter, key) => {counter[key] = 1 + counter[key] || 1; return counter},
{}
);
Sort the counter object using sort on Object.entries and finally show only keys.
const arr = ["apples", "oranges", "oranges", "oranges",
"bananas", "bananas", "oranges"
];
// create a counter object on array
let counter = arr.reduce(
(counter, key) => {
counter[key] = 1 + counter[key] || 1;
return counter
}, {});
console.log(counter);
// {"apples": 1, "oranges": 4, "bananas": 2}
// sort counter by values (compare position 1 entries)
// the result is an array
let sorted_counter = Object.entries(counter).sort((a, b) => b[1] - a[1]);
console.log(sorted_counter);
// [["oranges", 4], ["bananas", 2], ["apples", 1]]
// show only keys of the sorted array
console.log(sorted_counter.map(x => x[0]));
// ["oranges", "bananas", "apples"]

How to get a value of column 'b' from a 2d array where column 'a' matches a single array in javascript

New to javascript. Need some guidance on the problem below:
I have these arrays:
array1 = [['a1'], ['a2'], ['a3']];
array2 = [['a1',4], ['a3',3], ['a6',2]];
How can i get the matching arrays whereby array1 first col = array2 first col?
Example expected result:
[['a1',4], ['a3',3]]
I hope the question makes sense. Not really sure how to approach this since the structure of these two arrays are different.
You can use filter to filter out the elements. Inside filter method check if elements exist in array1. You can flat array1 to check effeciently.
let flatArr1 = array1.flat(); //["a1", "a2", "a3"]
const result = array2.filter(([x,y]) => flatArr1.includes(x));
console.log(result) // for instance
This is my solution for your problem:
const compare = (array1, array2) => {
let matched = [];
const keys = [].concat(...array1);
for(let i = 0; i < array2.length; i++) {
for(let j = 0; j < array2.length; j++) {
let result = array2[i][j];
if(keys.includes(result)) {
matched.push(array2[i])
}
}
}
console.log("matched: ", matched);
};

Get the unique value from 3 or more arrays [duplicate]

This question already has answers here:
Completely removing duplicate items from an array
(11 answers)
Closed 1 year ago.
In this case I have 3 arrays in javascript:
var array1 = ['124','10','100','190','1000'];
var array2 = ['124','100','190', '45'];
var array3 = ['124','100','175','19','1900'];
I need a script that get the unique values from 3 or more arrays (in this case 3 arrays). the result should be:
['10','1000','45','175','19','1900']
Thanks for the help
You could take a Map and set known keys to value false and vice versa.
Then map the map, filter and map only the keys.
In pieces:
Make a single array with all values from the arrays with spread syntax ....
Reduce the array wih a Map and set unseen values to true and seen values to false. The result ia a map with all values as key and as value either true or false, depending on uniqueness or not.
By having a map, you need to take only the keys with a value true. This requires an array from the map.
Filter the array to get only unique keys.
Map the array to get only the key.
The result is an array with unique values.
const
a = ['124', '10', '100', '190', '1000'],
b = ['124', '100', '190', '45'],
c = ['124', '100', '175', '19', '1900'],
unique = Array
.from([...a, ...b, ...c].reduce((m, v) => m.set(v, !m.has(v)), new Map))
.filter(([, b]) => b)
.map(([v]) => v);
console.log(unique);
This is quite elegant solution to this problem. It looks for global unique values even in the same array.
var array1 = ['124','10','100','190','1000'];
var array2 = ['124','100','190', '45'];
var array3 = ['124','100','175','19','1900'];
function getUniqueValues(...arrays) {
const concatetedArray = arrays.flat();
return arrays.reduce((accumulator, current) => {
return [...accumulator, ...current.filter((currentItem) => concatetedArray.indexOf(currentItem) === concatetedArray.lastIndexOf(currentItem))];
}, [])
}
console.log(getUniqueValues(array1, array2, array3));
A solution is to use an object like this:
var array1 = ['124', '10', '100', '190', '1000'];
var array2 = ['124', '100', '190', '45'];
var array3 = ['124', '100', '175', '19', '1900'];
const obj = {};
for (let i = 0; i < array1.length; i++) {
if (!obj[array1[i]]) {
obj[array1[i]] = 0;
}
obj[array1[i]]++;
}
for (let i = 0; i < array2.length; i++) {
if (!obj[array2[i]]) {
obj[array2[i]] = 0;
}
obj[array2[i]]++;
}
for (let i = 0; i < array3.length; i++) {
if (!obj[array3[i]]) {
obj[array3[i]] = 0;
}
obj[array3[i]]++;
}
const uniques = [];
Object.keys(obj).forEach(key => {
if (obj[key] == 1) {
uniques.push(key);
}
})
console.log(uniques);

Check whether elements from one array are present in another array

What is the best way to check whether elements from one array are present in another array using JavaScript?
I come up with two of the following methods (but neither of them do I like very much).
Method1
for(let i = 0; i < arr1.length; ++i) {
for(let j = 0; j < arr2.length; ++j) {
if(arr1[i] === arr2[j]) {
arr1[i].isPresentInArr2 = true;
break;
}
}
}
Method2
const idToObj = {};
for(let i = 0; i < arr2.length; ++i) {
nameToObj[arr2[i].Id] = arr2[i];
}
for(let i = 0; i < arr1.length; ++i) {
if(nameToObj[arr1[i].Id]) {
nameToObj[arr1[i].Id].isPresentInArr2 = true;
}
}
Here I am assuming that I have two arrays of objects: arr1 and arr2. Those objects have a unique Id property each. And I am to check whether every object in arr1 is present in arr2.
I suppose the second method would be more efficient. Hope for interesting suggestions.
in terms of Algorithm Complexity wise , It's like trade-offs .
First One - Space time Complexity - 0, Run time complexity - 0(n2)
Second One - Space time Complexity - o(n), Run time complexity - 0(n)
If it's performance focussed , go for second one .
In terms of js way , you have many ways . Read about includes() and indexOf() inbuilt method in javascript to avoid writing a loop . Also make use of javascript map function . You could also uses underscore.js
Ref Check if an array contains any element of another array in JavaScript for more details .
You could sort the arrays, then check if they are equal to one another:
var array1 = [4, 304, 2032], // Some random array
array2 = [4, 2032, 304];
function areEqual(array1, array2) {
if (array1.sort().toString() == array2.sort().toString()) {
// They're equal!
document.getElementById("isEqual").innerHTML = ("Arrays are equal");
return true;
} else {
// They're not equal.
document.getElementById("isEqual").innerHTML = ("Arrays aren't equal");
return false;
}
}
areEqual(array1, array2);
<!DOCTYPE html>
<html>
<body>
<p id="isEqual"></p>
</body>
</html>
You could get a list of Ids, then sort the id's and compare the two array lists of Ids using JSON.stringify
// Test arrays to work with
const array1 = [{Id:10},{Id:11},{Id:13},{Id:12}]
const array2 = [{Id:10},{Id:11},{Id:12},{Id:13}]
const array3 = [{Id:10},{Id:11},{Id:12}]
function test(arr1, arr2) {
// Map of each array [0, 1, 2, etc...]
let map1 = arr1.map(i => i.Id)
let map2 = arr2.map(i => i.Id)
// Sort each array from lowest to highest
// Note: Some kind of sort is required if we are going to compare JSON values
map1.sort()
map2.sort()
// Test the mapped arrays against each other
return JSON.stringify(map1) === JSON.stringify(map2)
}
console.log(test(array1, array2))
console.log(test(array1, array3))
map over the arrays of objects to produce arrays of ids, then use every and includes to check the elements in one array against the elements of the other: ids1.every(el => ids2.includes(el)).
Here's a working example:
const arr1 = [{ id: 0 }, { id: 1 }, { id: 2 }];
const arr2 = [{ id: 0 }, { id: 1 }, { id: 2 }];
const arr3 = [{ id: 14 }, { id: 1 }, { id: 2 }];
const getIds = (arr) => arr.map(el => el.id)
function check(arr1, arr2) {
const ids1 = getIds(arr1);
const ids2 = getIds(arr2);
return ids1.every(el => ids2.includes(el));
}
console.log(check(arr1, arr2));
console.log(check(arr1, arr3));

javascript permutation generator with permutation length parameter

I've seen a few generators out there but they all make a squared matrix. For example, you give it a list of three items and it'll assume the output of the length is also three. However, I'd like to specify the items and the length.
Sound like an easy problem can't believe there isn't a library available for it. Would like to avoid writing this myself if there's a tested library out there. Any suggestions would be great.
Example of what i've found
var list = 'abc';
perms = permutations(list);
//you cannot define the length
Example
var list = 'abc';
var length = 3;
perms = permutations(list,length);
console.log(perms);
/* output
a,a,a
a,b,c
a,b,a
a,c,a
c,a,a
...
*/
I would like to be able to change length and should create permutations accordingly
length = 2
a,a
a,b
b,b
b,a
length = 4
a,a,a,a
a,a,a,b
....
You can imagine the length as representing the number of slots. Each slot has N possibilities, given that N is the number of elements in your initial list. So given three values [1,2,3], you will have a total of 3 x 3 x 3 = 27 permutations.
Here's my attempt. Comments included!
var list = [1,2,3];
var getPermutations = function(list, maxLen) {
// Copy initial values as arrays
var perm = list.map(function(val) {
return [val];
});
// Our permutation generator
var generate = function(perm, maxLen, currLen) {
// Reached desired length
if (currLen === maxLen) {
return perm;
}
// For each existing permutation
for (var i = 0, len = perm.length; i < len; i++) {
var currPerm = perm.shift();
// Create new permutation
for (var k = 0; k < list.length; k++) {
perm.push(currPerm.concat(list[k]));
}
}
// Recurse
return generate(perm, maxLen, currLen + 1);
};
// Start with size 1 because of initial values
return generate(perm, maxLen, 1);
};
var res = getPermutations(list, 3);
console.log(res);
console.log(res.length); // 27
fiddle
If you're looking for an answer based on performance, you can use the length of the array as a numerical base, and access the elements in the array based on this base, essentially replacing actual values from the base with the values in your array, and accessing each of the values in order, using a counter:
const getCombos = (arr, len) => {
const base = arr.length
const counter = Array(len).fill(base === 1 ? arr[0] : 0)
if (base === 1) return [counter]
const combos = []
const increment = i => {
if (counter[i] === base - 1) {
counter[i] = 0
increment(i - 1)
} else {
counter[i]++
}
}
for (let i = base ** len; i--;) {
const combo = []
for (let j = 0; j < counter.length; j++) {
combo.push(arr[counter[j]])
}
combos.push(combo)
increment(counter.length - 1)
}
return combos
}
const combos = getCombos([1, 2, 3], 3)
console.log(combos)
For smaller use cases, like the example above, performance shouldn't be an issue, but if you were to increase the size of the given array from 3 to 10, and the length from 3 to 5, you have already moved from 27 (33) combinations to 100,000 (105), you can see the performance difference here:
I wrote a little library that uses generators to give you permutations with custom items and number of elements. https://github.com/acarl005/generatorics
const G = require('generatorics')
for (let perm of G.permutation(['a', 'b', 'c'], 2)) {
console.log(perm);
}
// [ 'a', 'b' ]
// [ 'a', 'c' ]
// [ 'b', 'a' ]
// [ 'b', 'c' ]
// [ 'c', 'a' ]
// [ 'c', 'b' ]

Categories

Resources