Roman and Arabic numbers string array sorting in JS - javascript

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);

Related

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

sorting array by predefined number

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

Sort - Move numbers and special characters to the end

I am trying to sort an array which looks like this:
var arr = ["a", "c", "1", "e", "-", "4", "p", "$", "~", "6", "33", "h"];
the order I want to get should be:
var arr = ["a", "c", "e", "h", "p", "-", "~", "$", "1", "33", "4", "6"];
I already have a working solution but I am not sure if this is the best (fastest) way how to do it.
arr.sort(function(a, b) {
return a.localeCompare(b);
});
for (var i = 0; i < arrLength; i++) {
if (arr[i].localeCompare("a") >= 0) {
if (i > 0) {
Array.prototype.push.apply(arr, arr.splice(0, i));
}
break;
}
}
Yes, you should include the logic in the comparison function. This will be easier to understand than first sorting by one criteria and then shifting around parts of the array by some other criteria, and it will be reusable in other situations.
You want
arr.sort(function(a, b) {
var aIsAlphabetical = a.localeCompare("a") >= 0,
bIsAlphabetical = b.localeCompare("a") >= 0;
if (!aIsAlphabetical && bIsAlphabetical)
return 1;
if (aIsAlphabetical && !bIsAlphabetical)
return -1;
return a.localeCompare(b);
});
or, shortened:
arr.sort(function(a, b) {
return (b.localeCompare("a")>=0)-(a.localeCompare("a")>=0) || a.localeCompare(b);
});
If you're looking for fastest code, you should set up a benchmark at the targetted system with a good example array.

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