javascript sort in wrong order - javascript

I'm having trouble with a javascript (node.js) sort function not sorting things in the correct order all the time. I need it to sort through negative and positive numbers and return the smallest non-negative number first. This is what I'm using right now:
.sort(function(a, b){if(a >= 0){return a - b;}else{return 1;}});
But when there is only one positive number, and it's at the end which happens often, that number is sorted as second to last. Can I have some help with a better way to implement this?

This sorting function works because it only sorts numbers if a is on the same side of 0 as b is:
function SortNonNegativesFirst(a,b){
if((a>=0) == (b>=0)) // Checks if both numbers are on the same side of zero
return a-b;
return a<b; // One is negative, one is positive, so return whether a is negative
}
console.log([1,6,8,4,-3,5,-2,3].sort(SortNonNegativesFirst)); //[1, 3, 4, 5, 6, 8, -3, -2]

Here's one method using the ternary operator:
alert([8,5,-1,-8,-2,7,1,10,1,7,-3].sort(function(a,b) {
return a*b < 0 ? b : a-b;
}));
The ternary operator is a shortcut for if ... else, so the above is equivalent to writing:
alert([8,5,-1,-8,-2,7,1,10,1,7,-3].sort(function(a,b) {
if(a*b < 0) return b;
else return a-b;
}));
This code returns true if a and b have different signs (positive and negative):
if(a*b < 0) return b;
If b is positive, sort places it before the negative a. If b is negative, sort places it after the positive a.

Related

How to implement an algorithm to detect if a number has 2 consecutive digits?

I want to create a function that returns true if a number has consecutive digits or not,
example:
if the input is 11, it will return true
if the input is 21 it will return false
if the input is 323 it will return false because even though we have 3 repeated, they are not consecutive
My solution right now is to transform the number into an array and loop through the number one by one, if the next number is equal to the current number then we just return true. But this has a complexity time of O(n) and I was wondering if anyone can come with a better solution.
Thank you
There is an arguably better solution where you don't need to convert the number into a string or array of numbers/character. It works as follows:
Initialize a variable curr to -1.
Run a loop while num > 0 and do the following:
next_curr = num % 10
if next_curr == curr: return true
curr = next_curr
num = num / 10 (integer division)
If the loop completes, return false.
This is a one pass O(log n) time complexity algorithm where n is the input number. The space complexity is O(1)
Note that while your algorithm was also O(log n) time complexity, it did 2 passes, and had a space complexity of O(log n) too.
I haven't written JS for some time now, but here's a possible implementation of the above algorithm in JS:
function sameAdjacentDigits(num) {
// to deal with negative numbers and
// avoid potential problems when using Math.floor later
num = Math.abs(num)
let curr = -1
while (num > 0) {
const nextCurr = num % 10
if (nextCurr == curr) return true
curr = nextCurr
num = Math.floor(num / 10)
}
return false
}
Use some regex, and then check what was found via the matcher
numbers_match = /(00|11|22|33|44|55|66|77|88|99)/;
numbers_match.match("11")
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/match
Easiest way to execute this is by using regex. Not sure what would be effectiveness of algorithm, but solution could be
/(\d)\1/
Inspired from #Tschallacka's answer:
let numbers = [11,21,323];
let result = numbers.map(n=>{
let test = n.toString().match(/(00|11|22|33|44|55|66|77|88|99)/);
return test != null;
})
console.log(result);

Big Number Sorting in Javascript

Let me know the difference between two code snippets for sorting an Array.
Snippet 1 -
function bigSorting(unsorted) {
return unsorted.sort((a, b) => a - b);
}
Snippet 2 -
function bigSorting(unsorted) {
return unsorted.sort((a, b) => {
if (a.length == b.length) {
return a > b ? 1 : -1;
}
return a.length - b.length;
});
}
I am having this file - 1000 Items to be in an Array, Snippet 2 will be able to sort the data but Snippet 1 won't.
FYI -
Problem Statement -
Consider an array of numeric strings where each string is a positive number with anywhere from to digits. Sort the array's elements in non-decreasing, or ascending order of their integer values and return the sorted array.
Example
Return the array ['1', '3', '150', '200'].
Function Description
Complete the bigSorting function in the editor below.
bigSorting has the following parameter(s):
string unsorted[n]: an unsorted array of integers as strings
Returns
string[n]: the array sorted in numerical order
Input Format
The first line contains an integer, , the number of strings in .
Each of the subsequent lines contains an integer string, .
Constraints
Each string is guaranteed to represent a positive integer.
There will be no leading zeros.
The total number of digits across all strings in is between and (inclusive).
Use BigInt() to sort them as numbers:
function bigSorting(unsorted) {
return unsorted.sort((a, b) => (BigInt(a) > BigInt(b))? 0 : -1 );
}
bigSorting(unsorted).forEach(i=>console.log(i));
Updated BIN:
Working solution in JS Bin

How is version number comparison working correctly in JavaScript?

I am able to compare version numbers correctly in JavaScript without having to split and check each decimal numbers. How is it working?
("2.0.1" > "2.1.0")
false
("2.2.1" > "2.1.0")
true
("2.5.1" > "2.0.5")
true
Thanks.
No, you're not " able to compare version numbers correctly in JavaScript without having to split"
"2.2.8" > "2.2.10" // true
Those strings are compared character after character, from left to right.
You do need to split and compare number after number, which is easy enough. Here's for example how you could implement it:
function Version(s){
this.arr = s.split('.').map(Number);
}
Version.prototype.compareTo = function(v){
for (var i=0; ;i++) {
if (i>=v.arr.length) return i>=this.arr.length ? 0 : 1;
if (i>=this.arr.length) return -1;
var diff = this.arr[i]-v.arr[i]
if (diff) return diff>0 ? 1 : -1;
}
}
console.log((new Version("1.1.1")).compareTo(new Version("1.2.1"))); // -1
console.log((new Version("1.1.1")).compareTo(new Version("1.10.1"))); // -1
console.log((new Version("1.10.1.2")).compareTo(new Version("1.10.1"))); // 1
console.log((new Version("1.10.1.2")).compareTo(new Version("1.10.1.2"))); // 0
Because you're comparing strings lexicographically, which yields the same result in your examples. However, this won't work in all circumstances, like when you get into double digits: 2.15.29.
I know this is old and already have a marked answer, but the below code works pretty well for me using localeCompare.
The function will return either:
0: the version strings are equal
1: the version a is greater than b
-1: the version b is greater than a
function sort(a, b){
return a.localeCompare(b, undefined, { numeric: true, sensitivity: 'base' })
}
The above works for angularJS and Javascript. The localeCompare() is an ES1 feature that is supported by all browsers.
For more detail on the usage refer to - https://www.w3schools.com/jsref/jsref_localecompare.asp
better way to compare is to create a version number float and then a sub version number, like shown below
subVersion = parseInt(fullVersion.split(".")[2]);
mainVersion = parseFloat(fullOsVer);
after this conversion, you can do comparison. this comparison will be comparing two integers.

Negation operator (!) used on a recursive call?

I can't figure out how this recursive call works. Using the not operator in the recursive call somehow makes this function determine if the argument given is odd or even. When the '!' is left out fn(2) and fn(5) both return true.
This example is taken out of JavaScript Allonge free e-book, which, so far has been excellent.
var fn = function even(n) {
if (n === 0) {
return true;
}
else return !even(n - 1);
}
fn(2); //=> true
fn(5); //=> false
If n === 0 the result is true.
If n > 0 it returns the inverse of n - 1.
If n === 1 it will return !even(0), or false.
If n === 2 it will return !even(1), or !!even(0), or true.
If n === 3 it will return !even(2), or !!even(1), or !!!even(0), or false.
And so on...
In general:
If n is even, the result is inverted an even number number of times, meaning it will return true.
If n is odd, the result is inverted an odd number number of times, meaning it will return false.
The above function reurns recursively the negation of it self.The base-case is when the number provided becomes zero and each time the function calls it self the number is decreased by one. As a result we have n recursive negations starting with true at base-case (where n is the number provided). For an odd number of negations given true as a starting value you get false as the result and for an even number you get true.
In summary:
Starting from given n
recursive reduction of n
Basecase: n=0 returns true
recursive negation of returned value(starting from true at base-case)
Result:
for odd number of negations the value returned is false
for even number of negations the value returned is true
Lets say we have example n=5
recursive reduction of n. Values of n at each level:
5
4
3
2
1
0 (base-case)
returned values at each level:
true (base case)
!true
!!true
!!!true
!!!!true
!!!!!true
A variant of you code could be:
function even(n) {
if (n === 0)
return true;
else
return odd(n - 1);
}
function odd(n) {
if (n === 1)
return true;
else
return even(n - 1);
}
We know that all positive numbers starting from 0 alternating between being even and odd. What you example does is defining odd to be !even which is correct since odd/even are disjoint. In your version using !, the not needs to be done as a continuation. That means every instance need to do something with the answer after the recursive call returns.

How to sort strings in JavaScript

I have a list of objects I wish to sort based on a field attr of type string. I tried using -
list.sort(function (a, b) {
return a.attr - b.attr
})
but found that - doesn't appear to work with strings in JavaScript. How can I sort a list of objects based on an attribute with type string?
Use String.prototype.localeCompare as per your example:
list.sort(function (a, b) {
return ('' + a.attr).localeCompare(b.attr);
})
We force a.attr to be a string to avoid exceptions. localeCompare has been supported since Internet Explorer 6 and Firefox 1. You may also see the following code used that doesn't respect a locale:
if (item1.attr < item2.attr)
return -1;
if ( item1.attr > item2.attr)
return 1;
return 0;
An updated answer (October 2014)
I was really annoyed about this string natural sorting order so I took quite some time to investigate this issue.
Long story short
localeCompare() character support is badass, just use it.
As pointed out by Shog9, the answer to your question is:
return item1.attr.localeCompare(item2.attr);
Bugs found in all the custom JavaScript "natural string sort order" implementations
There are quite a bunch of custom implementations out there, trying to do string comparison more precisely called "natural string sort order"
When "playing" with these implementations, I always noticed some strange "natural sorting order" choice, or rather mistakes (or omissions in the best cases).
Typically, special characters (space, dash, ampersand, brackets, and so on) are not processed correctly.
You will then find them appearing mixed up in different places, typically that could be:
some will be between the uppercase 'Z' and the lowercase 'a'
some will be between the '9' and the uppercase 'A'
some will be after lowercase 'z'
When one would have expected special characters to all be "grouped" together in one place, except for the space special character maybe (which would always be the first character). That is, either all before numbers, or all between numbers and letters (lowercase & uppercase being "together" one after another), or all after letters.
My conclusion is that they all fail to provide a consistent order when I start adding barely unusual characters (i.e., characters with diacritics or characters such as dash, exclamation mark and so on).
Research on the custom implementations:
Natural Compare Lite https://github.com/litejs/natural-compare-lite : Fails at sorting consistently https://github.com/litejs/natural-compare-lite/issues/1 and http://jsbin.com/bevututodavi/1/edit?js,console, basic Latin characters sorting http://jsbin.com/bevututodavi/5/edit?js,console
Natural Sort https://github.com/javve/natural-sort : Fails at sorting consistently, see issue https://github.com/javve/natural-sort/issues/7 and see basic Latin characters sorting http://jsbin.com/cipimosedoqe/3/edit?js,console
JavaScript Natural Sort https://github.com/overset/javascript-natural-sort: seems rather neglected since February 2012, Fails at sorting consistently, see issue https://github.com/overset/javascript-natural-sort/issues/16
Alphanum http://www.davekoelle.com/files/alphanum.js , Fails at sorting consistently, see http://jsbin.com/tuminoxifuyo/1/edit?js,console
Browsers' native "natural string sort order" implementations via localeCompare()
localeCompare() oldest implementation (without the locales and options arguments) is supported by Internet Explorer 6 and later, see http://msdn.microsoft.com/en-us/library/ie/s4esdbwz(v=vs.94).aspx (scroll down to localeCompare() method).
The built-in localeCompare() method does a much better job at sorting, even international & special characters.
The only problem using the localeCompare() method is that "the locale and sort order used are entirely implementation dependent". In other words, when using localeCompare such as stringOne.localeCompare(stringTwo): Firefox, Safari, Chrome, and Internet Explorer have a different sort order for Strings.
Research on the browser-native implementations:
http://jsbin.com/beboroyifomu/1/edit?js,console - basic Latin characters comparison with localeCompare()
http://jsbin.com/viyucavudela/2/ - basic Latin characters comparison with localeCompare() for testing on Internet Explorer 8
http://jsbin.com/beboroyifomu/2/edit?js,console - basic Latin characters in string comparison : consistency check in string vs when a character is alone
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare - Internet Explorer 11 and later supports the new locales & options arguments
Difficulty of "string natural sorting order"
Implementing a solid algorithm (meaning: consistent but also covering a wide range of characters) is a very tough task. UTF-8 contains more than 2000 characters and covers more than 120 scripts (languages).
Finally, there are some specification for this tasks, it is called the "Unicode Collation Algorithm", which can be found at http://www.unicode.org/reports/tr10/. You can find more information about this on this question I posted https://softwareengineering.stackexchange.com/questions/257286/is-there-any-language-agnostic-specification-for-string-natural-sorting-order
Final conclusion
So considering the current level of support provided by the JavaScript custom implementations I came across, we will probably never see anything getting any close to supporting all this characters and scripts (languages). Hence I would rather use the browsers' native localeCompare() method. Yes, it does have the downside of being non-consistent across browsers but basic testing shows it covers a much wider range of characters, allowing solid & meaningful sort orders.
So as pointed out by Shog9, the answer to your question is:
return item1.attr.localeCompare(item2.attr);
Further reading:
https://softwareengineering.stackexchange.com/questions/257286/is-there-any-language-agnostic-specification-for-string-natural-sorting-order
How to sort strings in JavaScript
Natural sort of alphanumerical strings in JavaScript
Sort Array of numeric & alphabetical elements (Natural Sort)
Sort mixed alpha/numeric array
https://web.archive.org/web/20130929122019/http://my.opera.com/GreyWyvern/blog/show.dml/1671288
https://web.archive.org/web/20131005224909/http://www.davekoelle.com/alphanum.html
http://snipplr.com/view/36012/javascript-natural-sort/
http://blog.codinghorror.com/sorting-for-humans-natural-sort-order/
Thanks to Shog9's nice answer, which put me in the "right" direction I believe.
Answer (in Modern ECMAScript)
list.sort((a, b) => (a.attr > b.attr) - (a.attr < b.attr))
Or
list.sort((a, b) => +(a.attr > b.attr) || -(a.attr < b.attr))
Description
Casting a boolean value to a number yields the following:
true -> 1
false -> 0
Consider three possible patterns:
x is larger than y: (x > y) - (y < x) -> 1 - 0 -> 1
x is equal to y: (x > y) - (y < x) -> 0 - 0 -> 0
x is smaller than y: (x > y) - (y < x) -> 0 - 1 -> -1
(Alternative)
x is larger than y: +(x > y) || -(x < y) -> 1 || 0 -> 1
x is equal to y: +(x > y) || -(x < y) -> 0 || 0 -> 0
x is smaller than y: +(x > y) || -(x < y) -> 0 || -1 -> -1
So these logics are equivalent to typical sort comparator functions.
if (x == y) {
return 0;
}
return x > y ? 1 : -1;
Since strings can be compared directly in JavaScript, this will do the job:
list.sort(function (a, b) {
return a.attr < b.attr ? -1: 1;
})
This is a little bit more efficient than using
return a.attr > b.attr ? 1: -1;
because in case of elements with same attr (a.attr == b.attr), the sort function will swap the two for no reason.
For example
var so1 = function (a, b) { return a.atr > b.atr ? 1: -1; };
var so2 = function (a, b) { return a.atr < b.atr ? -1: 1; }; // Better
var m1 = [ { atr: 40, s: "FIRST" }, { atr: 100, s: "LAST" }, { atr: 40, s: "SECOND" } ].sort (so1);
var m2 = [ { atr: 40, s: "FIRST" }, { atr: 100, s: "LAST" }, { atr: 40, s: "SECOND" } ].sort (so2);
// m1 sorted but ...: 40 SECOND 40 FIRST 100 LAST
// m2 more efficient: 40 FIRST 40 SECOND 100 LAST
You should use > or < and == here. So the solution would be:
list.sort(function(item1, item2) {
var val1 = item1.attr,
val2 = item2.attr;
if (val1 == val2) return 0;
if (val1 > val2) return 1;
if (val1 < val2) return -1;
});
Nested ternary arrow function
(a,b) => (a < b ? -1 : a > b ? 1 : 0)
I had been bothered about this for long, so I finally researched this and give you this long winded reason for why things are the way they are.
From the spec:
Section 11.9.4 The Strict Equals Operator ( === )
The production EqualityExpression : EqualityExpression === RelationalExpression
is evaluated as follows:
- Let lref be the result of evaluating EqualityExpression.
- Let lval be GetValue(lref).
- Let rref be the result of evaluating RelationalExpression.
- Let rval be GetValue(rref).
- Return the result of performing the strict equality comparison
rval === lval. (See 11.9.6)
So now we go to 11.9.6
11.9.6 The Strict Equality Comparison Algorithm
The comparison x === y, where x and y are values, produces true or false.
Such a comparison is performed as follows:
- If Type(x) is different from Type(y), return false.
- If Type(x) is Undefined, return true.
- If Type(x) is Null, return true.
- If Type(x) is Number, then
...
- If Type(x) is String, then return true if x and y are exactly the
same sequence of characters (same length and same characters in
corresponding positions); otherwise, return false.
That's it. The triple equals operator applied to strings returns true iff the arguments are exactly the same strings (same length and same characters in corresponding positions).
So === will work in the cases when we're trying to compare strings which might have arrived from different sources, but which we know will eventually have the same values - a common enough scenario for inline strings in our code. For example, if we have a variable named connection_state, and we wish to know which one of the following states ['connecting', 'connected', 'disconnecting', 'disconnected'] is it in right now, we can directly use the ===.
But there's more. Just above 11.9.4, there is a short note:
NOTE 4
Comparison of Strings uses a simple equality test on sequences of code
unit values. There is no attempt to use the more complex, semantically oriented
definitions of character or string equality and collating order defined in the
Unicode specification. Therefore Strings values that are canonically equal
according to the Unicode standard could test as unequal. In effect this
algorithm assumes that both Strings are already in normalized form.
Hmm. What now? Externally obtained strings can, and most likely will, be weird unicodey, and our gentle === won't do them justice. In comes localeCompare to the rescue:
15.5.4.9 String.prototype.localeCompare (that)
...
The actual return values are implementation-defined to permit implementers
to encode additional information in the value, but the function is required
to define a total ordering on all Strings and to return 0 when comparing
Strings that are considered canonically equivalent by the Unicode standard.
We can go home now.
tl;dr;
To compare strings in javascript, use localeCompare; if you know that the strings have no non-ASCII components because they are, for example, internal program constants, then === also works.
An explanation of why the approach in the question doesn't work:
let products = [
{ name: "laptop", price: 800 },
{ name: "phone", price:200},
{ name: "tv", price: 1200}
];
products.sort( (a, b) => {
{let value= a.name - b.name; console.log(value); return value}
});
> 2 NaN
Subtraction between strings returns NaN.
Echoing Alejadro's answer, the right approach is:
products.sort( (a,b) => a.name > b.name ? 1 : -1 )
A typescript sorting method modifier using a custom function to return a sorted string in either ascending or descending order
const data = ["jane", "mike", "salome", "ababus", "buisa", "dennis"];
const sortStringArray = (stringArray: string[], mode?: 'desc' | 'asc') => {
if (!mode || mode === 'asc') {
return stringArray.sort((a, b) => a.localeCompare(b))
}
return stringArray.sort((a, b) => b.localeCompare(a))
}
console.log(sortStringArray(data, 'desc'));// [ 'salome', 'mike', 'jane', 'dennis', 'buisa', 'ababus' ]
console.log(sortStringArray(data, 'asc')); // [ 'ababus', 'buisa', 'dennis', 'jane', 'mike', 'salome' ]
There should be ascending and descending orders functions
if (order === 'asc') {
return a.localeCompare(b);
}
return b.localeCompare(a);
If you want to control locales (or case or accent), then use Intl.collator:
const collator = new Intl.Collator();
list.sort((a, b) => collator.compare(a.attr, b.attr));
You can construct a collator like:
new Intl.Collator("en");
new Intl.Collator("en", {sensitivity: "case"});
...
See the above link for documentation.
Note: unlike some other solutions, it handles null, undefined the JavaScript way, i.e., moves them to the end.
Use sort() straightforward without any - or <
const areas = ['hill', 'beach', 'desert', 'mountain']
console.log(areas.sort())
// To print in descending way
console.log(areas.sort().reverse())
In your operation in your initial question, you are performing the following operation:
item1.attr - item2.attr
So, assuming those are numbers (i.e. item1.attr = "1", item2.attr = "2") You still may use the "===" operator (or other strict evaluators) provided that you ensure type. The following should work:
return parseInt(item1.attr) - parseInt(item2.attr);
If they are alphaNumeric, then do use localCompare().
list.sort(function(item1, item2){
return +(item1.attr > item2.attr) || +(item1.attr === item2.attr) - 1;
})
How they work samples:
+('aaa'>'bbb')||+('aaa'==='bbb')-1
+(false)||+(false)-1
0||0-1
-1
+('bbb'>'aaa')||+('bbb'==='aaa')-1
+(true)||+(false)-1
1||0-1
1
+('aaa'>'aaa')||+('aaa'==='aaa')-1
+(false)||+(true)-1
0||1-1
0
<!doctype html>
<html>
<body>
<p id = "myString">zyxtspqnmdba</p>
<p id = "orderedString"></p>
<script>
var myString = document.getElementById("myString").innerHTML;
orderString(myString);
function orderString(str) {
var i = 0;
var myArray = str.split("");
while (i < str.length){
var j = i + 1;
while (j < str.length) {
if (myArray[j] < myArray[i]){
var temp = myArray[i];
myArray[i] = myArray[j];
myArray[j] = temp;
}
j++;
}
i++;
}
var newString = myArray.join("");
document.getElementById("orderedString").innerHTML = newString;
}
</script>
</body>
</html>
var str = ['v','a','da','c','k','l']
var b = str.join('').split('').sort().reverse().join('')
console.log(b)

Categories

Resources