sorting array by predefined number - javascript

Let's say I have multidimensional array
var arr = [{
"id": "1",
"firstname": "SUSAN",
"dezibel": "91"
}, {
"id": "2",
"firstname": "JOHNNY",
"dezibel": "74"
}, {
"id": "3",
"firstname": "ANDREA",
"dezibel": "67"
}];
How can I sort it by "dezibel" but not ascending or descending, but closest to a giving number? For example,
var num = 78;
so target value is 78. and final sorting must be: 74, 67, 91.

You'll need to use a custom sort function that compares the absolute difference of each object's dezibel attribute from 78.
var arr = [{
"id": "1",
"firstname": "SUSAN",
"dezibel": "91"
}, {
"id": "2",
"firstname": "JOHNNY",
"dezibel": "74"
}, {
"id": "3",
"firstname": "ANDREA",
"dezibel": "67"
}];
num = 78;
arr.sort(
function(first,second){
var a = Math.abs(num - (+first.dezibel));
var b = Math.abs(num - (+second.dezibel));
return a - b;
});
alert(JSON.stringify(arr));

Write a sort function which calculates the distance to your number:
arr.sort(function(a, b){
return Math.abs(num-a) - Math.abs(num-b);
});
Use this to sort the dezibel properties in your array. It will calculate the distance between each of them and num. It will then select the smaller of the two distances, and continue in this manner to sort the whole array.

Just sort by the absolute difference.
var arr = [{ "id": "1", "firstname": "SUSAN", "dezibel": "91" }, { "id": "2", "firstname": "JOHNNY", "dezibel": "74" }, { "id": "3", "firstname": "ANDREA", "dezibel": "67" }],
num = 78;
arr.sort(function (a, b) {
return Math.abs(a.dezibel - num) - Math.abs(b.dezibel - num);
});
document.write('<pre>' + JSON.stringify(arr, 0, 4) + '</pre>');

.sort optionally takes a function. The function takes 2 values at a time, and compares them:
If the first value should sort higher than the second, the function should return a positive number.
If the first value should sort lower than the second, the function should return a negative number.
If the values are equal, the function should returns 0.
So, if you wanted to sort by dezibel in ascending order, you could do
arr.sort(function(a,b){
return a.dezibel- b.dezibel;
});
However, you want to sort by dezibel's distance from some number. To find the magnitude of the difference from 78 and the dezibel value, take the absolute value of the difference:
Math.abs(78 - a.dezibel)
Now, if we want to sort based on that value for each object, we can take the difference of that Math.abs call for both a and b:
arr.sort(function(a,b){
return Math.abs(78 - a.dezibel) - Math.abs(78 - b.dezibel);
});

You can use the array sort function for this:
arr.sort(function(a, b) {
return num - 1 * a.dezibel + num - 1 * b.dezibel
})

I would just add a distance and then sort it ...
num=78
for e in arr
e.dist=Math.abs(e.dezibel - num)
arr.sort (l,r) =>
if l.dist > r.dist then 1 else -1
delete the dist after
but put in one line is ok, too
arr.sort (l,r) =>
if Math.abs(l.dezibel - num) > Math.abs(r.dezibel - num) then 1 else -1

Related

How to compare if two dates are closer to each other

I want to check if two dates are within a range of each other, and reorder my array. I need to compare the dates from an array with the current date.
I have this:
var currentDate = new Date(); /* current date = 2021/08/18 */
listOfObjects = [ { "user": "John", "date": "2021-08-20" }, { "user": "Bob", "date": "2021-08-17" }, { "user": "Joe", "date": "2021-08-09" } ]
The return shoud be like this:
[ { "user": "Bob", "date": "2021-08-17" }, { "user": "John", "date": "2021-08-20" }, { "user": "Joe", "date": "2021-08-09" } ]
In JavaScript, an array is sorted by sorting the textual representation of its items.
The default sort order is ascending, built upon converting the elements into strings, then comparing their sequences of UTF-16 code units values. (Source: Array.prototype.sort() - JavaScript | MDN)
See following example:
const numbers = [1, 30, 4, 21, 100000];
numbers.sort();
console.log(numbers);
We see that the output is the array with alphabetically sorted numbers:
1, 100000, 21, 30, 4
In most cases, this is not what we want (or what most people expect). To sort numbers numerically, we pass a custom compare function to sort:
function i_cmp(a, b) {
let d = a-b;
if (d < 0)
return -1;
if (d > 0)
return 1;
return 0;
}
numbers.sort(i_cmp);
console.log(numbers);
output:
1,4,21,30,100000
To sort an array by a criterion that depends on further conditions, it's handy to pass a function bound to runtime values that is created by another function. Here we sort items by their absolute distance of a fixed value x.
function d_cmp(x) {
return function(a, b) {
let d = Math.abs(a-x)-Math.abs(b-x);
if (d < 0)
return -1;
if (d > 0)
return 1;
return 0;
}
}
numbers.sort(d_cmp(50));
console.log(numbers);
output:
30,21,4,1,100000
Hemera already answered how to get date distances. The rest, accessing date attributes, should be easy to implement.
For a live demo of above code (combined) see: https://ideone.com/e7DaOx
You can subtract the dates and compare the results. Like new Date("2021-08-18") - new Date("2021-08-17") = 86400000 cause dates are saved as milliseconds from a random but standardize reference date.
Then you can use this difference by using Math.abs(number) as a condition for finding the nearest dates to the given one and put it in a simple sorting function like below:
function orderByDateDistance(nDate, nList){
// easy sorting by finding the current min (also working with max)
for(let tA=0;tA<nList.length-1;tA++){ // iterating over all except the last will be sorted
let tIndex = tA; // current index
let tDifference = Math.abs(nDate-new Date(nList[tA]["date"])); // current difference
for(let tB=tA+1;tB<nList.length;tB++){ // iterating over unsorted list part
if(Math.abs(nDate-new Date(nList[tB]["date"])) < tDifference){ // compare current difference with stored
tIndex = tB; // save index
tDifference = Math.abs(nDate-new Date(nList[tB]["date"])); // save value optional
}
}
// change items
let tBuffer = nList[tA]; // save current object
nList[tA] = nList[tIndex]; // copy next lowest object
nList[tIndex] = tBuffer; // copy buffered object
}
return nList; // optional returning
}
// Your example
console.log(
orderByDateDistance(
new Date("2021/08/18"),
[
{"user": "John", "date": "2021-08-20"},
{"user": "Bob", "date": "2021-08-17"},
{"user": "Joe", "date": "2021-08-09"}
]
)
);

Sort an array by element frequency of appearance

I'm looking for an efficient solution to sort an array depending of how many times an element appears
For example :
let values = ["10", "4", "4", "4", "7", "7"]
I think the best output would be something like [{number, frequency}, ...], which, in the example would look like this :
[{4, 3}, {7, 2}, {10, 1}]
I've seen a lot of ways to do it, but every solution just sort the array depending of the frequency, without any access on how many times the element appears.
At the moment I only have this code that I got from another StackOverflow topic (can't remember which one sorry)
var map = values.reduce(function(p, c) {
p[c] = (p[c] || 0) + 1;
return p;
}, {});
var newTypesArray = Object.keys(map).sort(function(a, b) {
return map[a] < map[b];
});
console.log(newTypesArray);
It's doing a great job at sorting depending on the frequency, but I can't access how many times an element is repeated. And I have no idea how to do it...
Any thoughts?
const arr = [1, 1, 1, 2, 2, 3];
// use reduce for that.
const result = arr.reduce((result, item) => {
const count = result[item];
if (count === undefined) {
result[item] = 1;
} else {
result[item] += 1;
}
return result;
}, {});
// You'll get the result similar to this: {[item]: [count]}
// And then you can transform it into entries array:
const entries = Object.entries(result);
// entries are [[item, count], ...];
// And then sort
const sorted = entries.sort((entryA, entryB) => entryA[1] - entryB[1]);
// You'll have ascending sorted array by count.
console.log(sorted);
Something like this might do the trick:
const data = ["10", "7", "5", "4", "4", "7", "7", "4", "5", "4"];
const result = data.reduce((r, c) => (r[c] = (r[c] || 0) + 1, r), {});
var keys=Object.keys(result).sort((a,b)=>result[b]-result[a]); // sorted keys
console.log(keys.map(k=>k+':'+result[k])); // output, ordered by keys
It is up to you how you want to return the results. result contains the count and keys the actual values.
You can use Object.entries() over the object you got from reduce now sorting the array of key-value pairs and finally mapping to the array of {number, frequency}:
let values = ["10", "4", "4", "4", "7", "7"]
var map = Object.entries(values.reduce(function(p, c) {
p[c] = (p[c] || 0) + 1;
return p;
}, {}))
.sort((a, b) => a[1] < b[1])
.map((a) => ({[a[0]]:a[1]}));
console.log(map);
I think the best output would be something like [{number, frequency}, ...]
Actually these are not valid JavaScript objects inside your output array, if you want to show them as pairs you can use an array of arrays instead of this, the result will be like this:
[[number, frequency], ...]
In your code you were almost there, but you just need to map the result of your first reduce call to get the desired pair:
let values = ["10", "4", "4", "4", "7", "7"];
var map = values.reduce(function(p, c) {
p[c] = (p[c] || 0) + 1;
return p;
}, {});
var newTypesArray = Object.keys(map).map(function(k) {
return [+k, map[k]];
}).sort((a,b) => b[1] - a[1]);
console.log(newTypesArray);

whats difference here with string and numbers in javascript arrays?

I am merging two sorted arrays in JavaScript. When I call function with two arrays having numbers it works fine, but when I call that function with strings then it does not work. Why?
function mergeSortedArrays(array1, array2) {
const mergedArray = [];
let array1Item = array1[0];
let array2Item = array2[0];
let i = 1;
let j = 1;
if (array1.length === 0) {
return array2;
}
if (array2.length === 0) {
return array1;
}
while (array1Item || array2Item) {
if (array2Item === undefined || array1Item < array2Item) {
mergedArray.push(array1Item);
array1Item = array1[i];
i++;
} else {
mergedArray.push(array2Item);
array2Item = array2[j];
j++;
}
}
console.log(mergedArray);
}
//working?
mergeSortedArrays([0, 3, 4, 12, 222], [3, 4, 6, 30]);
// not working why?
mergeSortedArrays(["0", "3", "4", "12", "222"], ["3", "4", "6", "30"]);
As said in the comments, strings in JS are compared lexically , so, "222" is smaller than "3".
A solution that I see that you can use, is this one:
After checking the arrays for nullity, then concat then into mergedArray, then use the JS function sort(), with the basic return of value1 - value2, that way it will sort the strings in the order you want, and also will work for numbers.
(Further read: Why is one string greater than the other when comparing strings in JavaScript?)
function mergeSortedArrays(array1, array2) {
let mergedArray = [];
if (array1.length === 0) {
return array2;
}
if (array2.length === 0) {
return array1;
}
mergedArray = array1.concat(array2)
mergedArray.sort(function(a, b) {
return a - b
})
console.log(mergedArray);
return mergedArray;
}
mergeSortedArrays([0, 3, 4, 12, 222], [3, 4, 6, 30]);
mergeSortedArrays(["0", "3", "4", "12", "222"], ["3", "4", "6", "30"]);
BUT, be knowing that this solution will only work as expected if the strings are representations of numbers ("1", "2",...), if it is something like "aa", "abc", "b" it probably won't work well and another solution may be needed. (something like this: https://stackoverflow.com/a/51169/8732818)
Sort for string works differently than numbers. Its based on the ASCII table values. For example "99" > "100000" return should return true

Roman and Arabic numbers string array sorting in JS

My array contains list of the Arabic and roman numbers as string. I would like to sort them by the order of Roman numbers ascending first then follows the Arabic numbers in ascending order.
I write the code as below,
var myarray = ["i", "ii", "iii", "xv", "x", "1", "2", "3", "5", "601", "vi", "vii", "88", "99", "201", "101", "xix", "125", "iv", "vi", "v", "xiv", "58"]
myarray.sort(function (a, b) {
try {
if (Number(a) != null)
a = Number(a);
if (Number(b) != null)
b = Number(b);
} catch (e) {}
if (a > b) {
return 1;
}
if (b > a) {
return -1;
}
if (a == b) {
return a.position - b.position;
}
});
console.log(myarray);
But the results are like,
Results: ii,iii,xv,x,1,2,3,5,v,vi,vii,vi,iv,xix,xiv,58,88,99,101,125,201,601,i
If I have not convert the string to numbers,
Results: 1,101,125,2,201,3,5,58,601,88,99,i,ii,iii,iv,v,vi,vi,vii,x,xiv,xix,xv
My expect result should be
Results: i,ii,iii,iv,v,vi,vi,vii,x,xiv,xv,xix,1, 2, 3,5,58,88,99,101,125,201,601
Multiple things here:
You don't need a try / catch block. Just leave it.
Checking the parsed number for null won't give you the expected result. You need to check for NaN.
Either parse both values to a number or none. Otherwise you can't compare properly.
Usually numbers will be returned before strings. To avoid this behaviour you need to invert the result value by multiplicating with -1, if one value is a number and one is a string.
Here is the working version of the sort method:
var myarray = ["i", "ii", "iii", "xv", "x", "1", "2", "3", "5", "601", "vi", "vii", "88", "99", "201", "101", "xix", "125", "iv", "vi", "v", "xiv", "58"]
myarray.sort(function (a, b) {
if (!isNaN(Number(a)) && !isNaN(Number(b))) {
a = Number(a);
b = Number(b);
}
var result;
if (a > b) {
result = 1;
}
if (b > a) {
result = -1;
}
if (a == b) {
result = a.position - b.position;
}
if (isNaN(Number(a)) !== isNaN(Number(b))) {
result = result * -1;
}
return result;
});
console.log(myarray);

How to sort (reverse) object of jsonparse?

I have object {"5":"5","4":"4","3":"3","2":"2","1":"1","-1":"P1",-2":"P2"}
And use this function to parse elements:
function floormake(inobj) {
var levels = '';
var obj = JSON.parse(inobj);
levels += '<ul>';
Object.keys(obj).sort(-1).forEach(function (key) {
levels += '<li>' + obj[key] + '</li>';
});
levels += '</ul>';
return levels;
}
But result alway sorting by number: -1, -2, 1, 2 etc. BUT i need reverse sorting: 5, 4, 3, 2, 1,
sort(-1) - doesn't work
Consider using .reverse() instead.
Object.keys(obj).sort().reverse().forEach( ....
Reverse documentation
Edit Note: As mentioned by #Shmiddty, the reverse() method does not actually sort. The array will need to be sorted then reversed.
The Array.sort method does not accept an integer as the only optional parameter. It accepts a reference to a function that either returns -1, 0, or 1, and the details can be found here: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/sort
Here's an example of how to sort based on number:
http://jsfiddle.net/3HFN5/1/
var a = ["5", "9", "1", "399", "23", "21"];
function sorter(b, c) {
return (+b < +c ? 1 : -1);
}
alert(a.sort(sorter));
Or something simpler/better:
http://jsfiddle.net/3HFN5/2/
var a = ["5", "9", "1", "399", "23", "21"];
function sorter(b, c) {
return c - b;
}
alert(a.sort(sorter));
And incorporating this with your actual example:
http://jsfiddle.net/3HFN5/3/
var a = {"2":"2","4":"4","-2":"P2","3":"3","300":"4","1":"1","5":"5","-1":"P1"};
function sorter(b, c) {
return c - b;
}
alert(Object.keys(a).sort(sorter));
I mixed the items in the object around and added one to prove it's sorting accurately/completely.

Categories

Resources