Array of Objects ordered by similarity to neighbour - javascript

I really hope you can help me I have an array of objects and I need an algorithm or a pointer to something to read up on that will sort them by similarity to their neighbours.
For Example
[
{a:12,b: 7,c: 5},
{a: 5,b: 5,c: 5},
{a: 3,b: 3,c: 3},
{a: 5,b: 7,c: 5},
{a:12,b: 7,c: 5}
]
becomes
[
{a: 5,b: 5,c: 5},
{a: 5,b: 7,c: 5},
{a:12,b: 7,c: 5},
{a:12,b: 7,c: 5},
{a: 3,b: 3,c: 3},
]
I have a REPL here...
https://repl.it/#idrise/ThoseWellmadeMonitors
I have brute forced it but it doesn't get the best score and it takes eons on big arrays.
This is how the score is calculated, the higher the score the better !
function scoreArray(array) {
let score = 0;
for (let f = 1; f < array.length; f++) {
score += howSimilarAreObjects(array[f - 1], array[f]);
}
return score;
}
function howSimilarAreObjects(object1, object2) {
let score = 0;
Object.keys(object1).forEach(curValue => {
if (object1[curValue] === object2[curValue]) {
score++;
}
});
return score;
}
Any help greatly appreciated,
Idris

You could run every element against each other and get the similarity between the both objects. Then take the groups and get the objects with the highest similarity first and then with the lower ones. By pushing them to the result set, filter the array by already seen objects.
const similar = (a, b) => Object.keys(a).filter(k => a[k] === b[k]).length;
var array = [{ a: 12, b: 7, c: 5}, { a: 5, b: 5, c: 5}, { a: 3, b: 3, c: 3}, { a: 5, b: 7, c: 5}, { a: 12, b: 7, c: 5}],
groups = {},
used = new Set,
result = [];
array.forEach((a, i) =>
array
.slice(i + 1)
.forEach(b => (s => (groups[s] = groups[s] || []).push(a, b))(similar(a, b))));
Object
.keys(groups)
.reverse()
.forEach(k => result.push(...groups[k].filter(o => !used.has(o) && used.add(o))));
console.log(result);
console.log(groups);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Let us define a function that you wish to minimize:
minimal sum of squared distance between every pair of neighbors
If you were to iterate over all n! permutations, and choose the lowest total, you would have achieved your goal. Of course, n! is way too expensive, so we have to use something simpler.
You can take a greedy approximation, which is simply:
start by any neighbor
continue with the nearest neighbor to the last-added one
repeat 2 until you finish
This will not be optimal, but will not be too far off either. And it is quite cheap to calculate (O(n^2), intead of O(n!)).
Using this function, you are essentially trying to solve the traveling salesman problem (TSP), on which a lot has been written, and for which a lot of approximations exist. The general problem is np-hard, so the normal approach is to choose a not-so-expensive approximation, of which the simplest may be the greedy approach outlined above.

Related

Find item between row and column in JavaScript

I want to find what's the position of the following array, according to the table below.
So, here is an example:
I have this array : ['AA','CC','TC']
and I have this table:
Now, according to the array, my first value is 'AA'. So if you notice, we have A and A as the fist index, which we have to read in the table below. So first, we look in the rows of the table, and then we check the columns. we see, that the number of the row and of the columns in the value:12.
So far I have this code in JavaScript:
function cal_extinction_coefficient(str) {
var values_compare= [['Values', 'A','C','T'], ['A', 12,14,16], ['C',23,25,26], ['T',31,34,37]];
var values_to_find=['AA','CC','TC']
let chunk=0;
var all_combLength= values_to_find.length;
for (var i = 0; i < all_combLength; i++) {
for (let j = 0; j < values_compare.length; j++) {
const final= times[j].includes(all_combinations[i].slice(0,1));
}
}
However, I don't know how to access these values... I have the same resolution, but in pandas and with python...
Can someone help me please?
If you are new to JS, try to create your own functions first. It's usually easier than it seems to be.
const data = {
dataFrame: [
['A', 27, 21],
['C', 21, 14]
],
columns: ['Type', 'A','C']
}
const getValue = (xy) => {
[col, row] = xy.split('');
return data.dataFrame.find(x => x[0] == row)?.[data.columns.indexOf(col)];
}
console.log(getValue("AA"));
You could use nested objects:
const type1 = {A: {A: 27, C: 21}, C: {A: 21, C: 14}}
and access your elements with
type1['A']['C']
or
type1.A.C
Using lodash#get you can access the element with
_.get(type1, ['A', 'C'])
Examples:
const type1 = {A: {A: 27, C: 21}, C: {A: 21, C: 14}};
console.log(type1['A']['C']);
console.log(type1.A.C);
console.log(_.get(type1, ['A', 'C']));
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.21/lodash.min.js"></script>

Top highest values in an object (more if there are more max values and they are the same)

Lets suppose I have object like this:
var obj = {a : 5, b : 10, c : 15, d : 20, e : 20, f : 25};
I would like to get top 3 highest values - notice that d and e key have the same value and I need to get the keys also, so it would looks like:
Highest values:
f - 25
d - 20
e - 20
also if there are for example six values and four are identical:
var obj2 = {a:1, b:1, c:1, d:1, e:0,8, f: 0,5};
I need to show 4 highest.
Highest values:
a-1
b-1
c-1
d-1
I guess there is need to iterate over ALL object properties to get Math.max, but I also need a something to count 3 max numbers WITH their keys, and if there is more max (all the same) I need to "get them all!".
EDIT: there are great answers atm, so I guess I will not finish this code and just use given examples :)
This is an example implementation, with annotations to explain what is happening at each step.
function maxValues(o, n) {
// Get object values and sort descending
const values = Object.values(o).sort((a, b) => b - a);
// Check if more values exist than number required
if (values.length <= n) return o;
// Find nth maximum value
const maxN = values[n - 1];
// Filter object to return only key/value pairs where value >= maxN
return Object.entries(o)
.reduce((o, [k, v]) => v >= maxN ? { ...o, [k]: v } : o, {});
}
const a = maxValues({
a: 5,
b: 10,
c: 15,
d: 20,
e: 20,
f: 25
}, 3);
console.log(a);
const b = maxValues({
a: 1,
b: 1,
c: 1,
d: 1,
e: 0.8,
f: 0.5
}, 3);
console.log(b);
const c = maxValues({
a: 5,
b: 10,
}, 3);
console.log(c);
The callback passed to the Array.prototype.reduce function can be expanded out to the following:
return Object.entries(o)
.reduce(function (obj, [key, value]) {
if (v >= maxN) {
return Object.assign(obj, {
[key]: value
});
} else {
return obj;
}
}, {});
Instead, I condensed it down using an Arrow Function Expression, ternary operator, and spread syntax.
The ternary operator is essentially shorthand for an if/else statement. E.g.
condition ? true : false;
// or
v >= maxN ? { ...o, [k]: v } : o;
The spread syntax allows an iterable value to be expanded in place. In this instance, it's being used to copy existing key/value pairs from one object literal to another.
const a = { first_name: 'Rob', gender: 'male' };
const b = { ...a, username: 'fubar' };
console.log(b); // { first_name: 'Rob', gender: 'male', username: 'fubar' };
Simply,
Sort the object based on its values using, Object.entries
Get the least value you can filter.
Filter the entries and return as Object using Object.fromEntries.
function getTopValues(obj, topN)
{
var sortedEntries = Object.entries(obj).sort(function(a,b){return b[1]-a[1]});
var last = sortedEntries[topN-1][1];
var result = sortedEntries.filter(function(entry){
return entry[1] >= last;
});
console.log(Object.fromEntries(result));
}
getTopValues({a:5, b:10, c:15, d:20, e:20, f:25}, 3);
getTopValues({a:1, b:1, c:1, d:1, e:0.8, f: 0.5}, 3);
getTopValues({a:1, b:1, c:1, d:1, e:0.8, f: 0.5}, 5);
So, you want to find the top 3 highest and if there are multiple identical highest then you want to include all of that.
This problem is asked in a slightly weird fashion.
I am going to assume that if there is something like a:1 b:1 c:2 d:2 e:3,
you would like to include a,b,c and d.
First of all, you only have to keep track of the keys because you can get the values instantly at the end.
Ok! Let's start. (efficient but ugly)
class Numandamount {
constructor(number, amount) {
this.number = number;
this.amount = amount;
}
}
//That's just a class to link numbers and their amounts
var numtoamount = [];
//Now let's fill that array!
for (var property in obj) {
if (obj.hasOwnProperty(property)) {
var num = obj.property;
var found = false;
for(Numandamount naa in numtoamount){
if(naa.number == num){
naa.amount++;
found = true;
}
}
if(!found){
naa.push(new Numandamount(num,1));
}
}
}
//The array is done!
numtoamount.sort(function(a,b){return b.number-a.number});
//Now all we have to do is loop through it!
var count = 0; // keep track of how many we did
var i = 0;
while(count<4 && i<numtoarray.length){
count += numtoamount[i].amount;
i++;
}
//BOOOM WE DID IT
// I is the biggest index so all we have to do is:
for(var j = 0;j<i;j++){
console.log("We have "+numtoamount[j].amount+" "+numtoamount[j].number+"'s");
}
For eg. it will print out for this example obj: {a:1 b:1 c:4 d:6 e:7 f:4}
We have 1 7's
We have 1 6's
We have 2 4's
If you would like some other implementation please comment below!
I put my heart into this <3
I would start with transforming your object into an array of objects:
const arr = []
for (var key in obj){
arr.push( {[key]: obj[key]} )
}
Now you have an array that looks like this:
[
{"f": 25},
{"d": 20},
{"e": 20},
{"c": 15},
{"b": 10},
{"a": 5}
]
Now you can sort your objects by the magnitude of their values:
const sortedArray = arr.sort( (a,b) => {
if (Object.values(a)[0] > Object.values(b)[0]) {
return -1
}
})
Which would give:
[
{"f": 25},
{"d": 20},
{"e": 20},
{"c": 15},
{"b": 10},
{"a": 5}
]
Then you can just pick however many values off the top you want. For example
sortedArray.filter( (item, index) => {
if (index <= 2 || Object.values(item)[0] === Object.values(sortedArray[0])[0]) {
return item
}
})
Which gives:
[
{"f": 25},
{"d": 20},
{"e": 20}
]
Or in the case of your second object, it would match the n highest values, but also grab any other values that are equal to the highest value.
As a single function:
function sortYourObject(object, number){
var arr = []
for (var key in object){
arr.push( {[key]: object[key]} )
}
const sortedArray = arr.sort( (a,b) => {
if (Object.values(a)[0] > Object.values(b)[0]) {
return -1
}
})
const endresult = sortedArray.filter( (item, index) => {
if (index <= 2 || Object.values(item)[0] === Object.values(sortedArray[0])[0]) {
return item
}
})
return endresult
}

How to remove duplicates from a typescript array? [duplicate]

This question already has answers here:
How to remove all duplicates from an array of objects?
(77 answers)
Closed 5 years ago.
Could you let me know how do I remove duplicates from an Array in type script.
My array will look something like
a = [{a: 1, b: 2}, {a: 1, b: 2}, {c: 3, d: 4}]
I am looking to get
a = [{a: 1, b: 2}, {c: 3, d: 4}]
I used Set data strucure like below
a = Array.from(new Set(a))
but still no use. Please let me know how to remove duplicates from an array using single statement?
Is not in a single statement but is short.
var a = [{a: 1, b: 2}, {a: 1, b: 2}, {c: 3, d: 4}];
a = a.filter((value, index, array) =>
!array.filter((v, i) => JSON.stringify(value) == JSON.stringify(v) && i < index).length);
console.log(a);
Your question sees like this:
Delete duplicated elements in array of objects Javascript
But like in the comment will fails for:
var a = [{a: 1, b: 2}, {b: 2, a: 1}];
You need a custom compare for your case:
function isEqual(a, b){
for(var i in a)
if(a[i] != b[i])
return false;
for(var i in b)
if(b[i] != a[i])
return false;
return true;
}
var a = [{a: 1, b: 2}, {b: 2, a: 1}, {c: 3, d: 4}];
a = a.filter((value, index, array) =>
!array.filter((v, i) => isEqual(value, v) && i < index).length);
console.log(a);
You can compare ids or somenthing like this to identify equal object in this sample i just compare the properties.
Like #Juan Mendes said in comment:
The reason your code doesn't filter elements is because two similar objects are still considered different objects because they point to different objects. You need to write your own code that uses a custom comparator.

Special sorting of an array of objects

So theres tons of posts on sorting something like this:
var arr = [{a: 1}, {b: 2}] alphabetically by key, but what if you have something like var arr = [{a: 100}, {a: 50}], what I want is to then say "oh you're the same? lets sort you then by value (which will always be a number).
I am unsure how to do either in lodash or any other similar javascript way.
The end result should be either:
[{b: 2}, {a: 1}] // Keys are different (alphabetical)
// or:
[{a: 50}, {a: 100}] // Keys are the same (lowest to highest)
Everything I have seen on stack overflow becomes a quick mess (code wise), and I was thinking, there are sort methods on lodash, but how exactly do I achieve what I want given the circumstances ??
Any ideas?
Some one asked a good question, are there more keys? Are they only a and b?
There would only be two objects in this array at any given time and yes the keys would only ever be strings, in this case the strings could be anything, cat, dog, mouse, apple, banana ... What ever.
The values will only ever be numbers.
Clearing the air
If the keys match, only sort the array by value, if the keys do not match, only sort the array by key. There will only ever be two objects in this array. Apologies for the misunderstanding.
You can use only one function to perform the two types of sorting (works for your case, in which you have only an array with two items, but it is completely generic regarding the array length):
var arr1 = [{a: 30}, {a: 2}];
var arr2 = [{b: 30}, {a: 2}];
function sortArr(arr) {
return arr.sort((a, b) => {
var aKey = Object.keys(a)[0];
var bKey = Object.keys(b)[0];
return (aKey !== bKey) ? aKey.localeCompare(bKey) : a[aKey] - b[bKey];
});
}
var sortedArr1 = sortArr(arr1);
var sortedArr2 = sortArr(arr2);
console.log(sortedArr1);
console.log(sortedArr2);
In case you always have one property in your objects you can first sort by key using localeCompare and then by value of that property.
var arr = [{b: 2}, {b: 10}, {a: 1}, {c: 1}, {a: 20}]
arr.sort(function(a, b) {
var kA = Object.keys(a)[0]
var kB = Object.keys(b)[0]
return kA.localeCompare(kB) || a[kA] - b[kB]
})
console.log(arr)
Before sorting you can create array of unique keys that you can use to check if all object have the same key by checking if length is > 1 and use that in sort function.
var arr = [{b: 10}, {b: 2}, {a: 1}, {c: 1}, {a: 20}, {b: 22}]
var arr2 = [{a: 10}, {a: 2}, {a: 1}, {a: 22}]
function customSort(data) {
var keys = [...new Set([].concat(...data.map(e => Object.keys(e))))]
data.sort(function(a, b) {
var kA = Object.keys(a)[0]
var kB = Object.keys(b)[0]
return keys.length > 1 ? kA.localeCompare(kB) : a[kA] - b[kB]
})
return data;
}
console.log(customSort(arr))
console.log(customSort(arr2))
var arr = [{b: 2}, {a: 1}, {b: 1}, {a: 100}, {a: 20}];
arr.sort(function(a, b) {
var aKey = a.hasOwnProperty("a")? "a": "b", // get the first object key (if it has the property "a" then its key is "a", otherwise it's "b")
bKey = b.hasOwnProperty("a")? "a": "b"; // same for the second object
return aKey === bKey? // if the keys are equal
a[aKey] - b[aKey]: // then sort the two objects by the value of that key (stored in either aKey or bKey)
aKey.localeCompare(bKey); // otherwise sort by the strings aKey and bKey (the keys of the two objects)
});
console.log(arr);

Performance of indexOf() vs. an Associative Array in Javascript?

I have a series of values which correspond to certain numbers. There are never two values that correspond to the same number. There are no gaps between the numbers.
E.G.
{a: 1, b: 2, c: 3, d: 4}
as opposed to
{a: 1, b: 3, c: 4, d: 7}
Right now, I store the values in an array and find the index of that value. The index in the array is the number that value corresponds with.
Assume the arrays are prepopulated. My questions is which is more efficient to find the corresponding numbers in Javascript:
Store the values in specific positions of an array and find the index with indexOf().
---or---
Store the values and numbers as key value pairs in an associative array.
from what you are saying...
a should be zero as the array starts from index zero and not 1
CASE numeric array:{a: 1, b: 2, c: 3, d: 4}
for loop is suitable
var a = [];
a[5] = 5;
for (var i=0; i<a.length; i++) {
// Iterates over every single numeric indexes from 0 to 5
}
CASE a value is skipped:{a: 1, b: 3, c: 4, d: 7}
for..in is suitable
var a = [];
a[5] = 5;
for (var x in a) {
ignores 0-4,shows only 5
}
CASE Associative array: indexof is suitable
NOTE: It is considered bad practice for iterating through arrays,but not for iterating through members of an object.

Categories