I am attempting to write a JavaScript function, OneLetterOff, that will take in a String, and an Array of accepted words (WordList).
It should return an array of words from the WordList that only differ from the word given in the String by only one letter, at a single position.
For example:
WordList = ["marc", "bark", "parc", "shark", "mark"];
OneLetterOff("park", WordList); // should return ["bark", "parc", "mark"]
Words that pass the test have to be of the same length, and we can safely assume they are all lower case letters.
How do I use Regular Expressions to solve this algorithm? Essentially, are there ways other than having to use Regular Expressions to solve it?
Thank you so much for your help.
Regular expressions are not the best for it but to give you an idea:
"mark".match(/.ark|p.rk|pa.k|par./) //true
You can, of course, build regular expressions automatically and just "." might not be what you are looking for, depending on the possible characters you need to include.
I suggest you figure out the rest on your own as it looks a lot like homework ;-)
There are many non-regexp ways to solve it. For short words pre-compiled regexp will probably be the most efficient though.
You are looking for words in a list with a Levenshtein distance of 1 from a given word.
As found at Algorithm Implementation/Strings/Levenshtein distance, a JavaScript implementation of the algorithm is as follows:
function levenshteinDistance (s, t) {
if (s.length === 0) return t.length;
if (t.length === 0) return s.length;
return Math.min(
levenshteinDistance(s.substr(1), t) + 1,
levenshteinDistance(t.substr(1), s) + 1,
levenshteinDistance(s.substr(1), t.substr(1)) + (s[0] !== t[0] ? 1 : 0)
);
};
Using that method with Array.prototype.filter (polyfill needed for IE<9) to include only items with a distance of 1, we get a very simple bit of code:
var oneLetterOff = function (word, list) {
return list.filter(function (element) {
return levenshteinDistance(word, element) === 1;
});
};
oneLetterOff('park', ['marc', 'bark', 'parc', 'shark', 'mark']);
// returns ["bark", "parc", "mark"]
One great feature to this approach is that it works for any distance--just change what you're comparing to in the filter.
If you really wanted to use regular expressions (which I would not recommend for this), you would need to:
Iterate the given word to create a set of strings representing regular expression subpatterns where each has one char optional
Combine those string subpatterns into a regular expression using new RegExp()
Iterate the list of words testing them against the expresison
When you get a match, add it to a set of matches
Return the set of matches
It wouldn't take long to write, but given the answer I gave above I think you'll agree it would be a silly approach.
Here is my solution inspired by JAAuide and using all the power of JavaScript functions
function lDist (s, t) {
/* If called with a numeric `this` value
returns true if Levenshtein distance between strings s and t <= this
else
returns the Levenshtein distance between strings s and t */
return this.constructor === Number ? lDist.call (null, s, t) <= this :
s.length && t.length
? Math.min (lDist (s.slice (1), t) + 1,
lDist (t.slice (1), s) + 1,
lDist (s.slice (1), t.slice (1)) + (s.charAt (0) !== t.charAt (0)))
: (s.length || t.length) };
['marc', 'bark', 'parc', 'shark', 'mark'].filter (lDist.bind (1, 'park'));
See the jsFiddle
Related
I am trying tor write this function that Check if a string (first argument, str) ends with the given target string (second argument, target). I have used this code but it seems not to work. How can i tweak it?
function confirmEnding(str, target) {
var last = str.substring(-1);
var last2 = target.substring(-1);
if (last == last2) return true;
else if (last !== last2) return false;
}
confirmEnding("Walking on water and developing software from a specification
are easy if both are frozen", "specification") )/*should return "false".
confirmEnding("Bastian", "n") should return true.
confirmEnding("Connor", "n") should return false.
confirmEnding("Walking on water and developing software from a specification
are easy if both are frozen", "specification") should return false.
confirmEnding("He has to give me a new name", "name") should return true.
confirmEnding("Open sesame", "same") should return true.
confirmEnding("Open sesame", "pen") should return false.
confirmEnding("If you want to save our world, you must hurry. We dont know
how much longer we can withstand the nothing", "mountain") should return
false.
Do not use the built-in method .endsWith() to solve the challenge.*/
In order to pass all of the tests with the desired return values, the function should not be comparing the last character of the string, but rather the entire string, target to the corresponding end substring of str. You need the length of target to find the correct starting index for the corresponding substring in str as follows:
function confirmEnding (str, target) {
return str.substr(-(target.length)) === target
}
Your code is comparing the entire strings. See substring() documentation below. -1 is defaulting to 0 thus returning the substring starting at index 0 and returning the rest of the string (the entire string) since no end index is given. .
"If either argument is less than 0 or is NaN, it is treated as if it
were 0."
You can use the substr() method instead of substring() if you want to use negative indices. substr() recognizes negative index values instead of defaulting to 0.
"If start is negative, substr() uses it as a character index from the
end of the string."
You can use the length of target and subtract it from the length of str to get the correct substring for comparison. This will return all of the characters from this index to the end of the string as in str.length - target.lengththough you only really need target.length to make the comparison using negative indices.
Using substring():
function confirmEnding (str, target) {
var last = str.substring(str.length-(target.length));
if (last == target ) return true;
else return false;
}
Using substr():
function confirmEnding (str, target) {
var last = str.substr(-(target.length));
if (last == target ) return true;
else return false;
}
or a cleaner/alternate implementation:
function confirmEnding (str, target) {
return str.substr(-(target.length) === target)
}
substr() documentation
substring() documentation
After seeing the ongoing confusion over this case (abbreviated for readability):
confirmEnding(
"Walking on water...both are frozen",
"specification"
); // Should return false (why not true?)
and also this interesting note:
/* Do not use the built-in method .endsWith() to solve the challenge. */
I have a hunch about what may have happened.
Double-check the instructions for this question. Are you sure you're supposed to test if the last character of each string is the same? It sounds like you are supposed to test if the src string ends with the entire target string.
After all, that is what the .endsWith() method does. And it explains the mystery of the test case above.
The MDN documentation for .endsWith() doesn't describe the method very well, but the examples it gives make it clear.
With that understanding, you can probably now write the code. I'm not going to write it for you, but I will drop some hints below. I added some code for your tests so that they not only log the result, but also whether they return the desired result. (In the version as written here, all the tests will fail.)
// Return true if str ends with target, false if it does not
function confirmEnding( str, target ) {
// You can do this in a single return statement
// with one === comparison in it. The .slice()
// method will help you here, and you only need
// to pass a single argument into it.
// You don't need any if statements, intermediate
// variables, or anything fancy.
// There are several other ways to do it too, including
// the approach shown on the MDN page.
}
function testEnding( str, target, desired ) {
var result = confirmEnding( str, target );
console.log(
'"' + str + '"',
'"' + target + '"',
'returns', result,
result === desired ? 'Good' : 'WRONG!'
);
}
testEnding( "Bastian", "n", true );
testEnding( "Connor", "n", false );
testEnding( "Walking on water and developing software from a specification are easy if both are frozen", "specification", false );
testEnding( "He has to give me a new name", "name", true );
testEnding( "Open sesame", "same", true );
testEnding( "Open sesame", "pen", false );
testEnding( "If you want to save our world, you must hurry ); We dont know how much longer we can withstand the nothing", "mountain", false );
You can use this function:
function confirmEnding(a, b) {
var l1 = a[a.length - 1];
var l2 = b[b.length - 1];
return l1 === l2;
}
Your error is that you're using substring. Try str.substr instead of substring
function confirmEnding (str, target) {
return str.substr(-1) == target.substr(-1);
}
console.log(confirmEnding("Walking on water and developing software from a specification are easy if both are frozen", "specification"));
const a = "Walking on water and developing software from a specification are easy if both are frozen",
b = "specification";
// your function
const equalLastLetter = (a, b) => a.substr(-1) === b.substr(-1);
console.log(equalLastLetter(a, b))
How about this?
function confirmEnding (str, target) {
var last = str.charAt(str.length-1);
var last2 = target.charAt(target.length-1);
return (last == last2);
}
You can use chatAt()
function confirmEnding (str, target) {
var last = str.charAt(str.length -1);
var last2 = target.charAt(target.length -1);
return last === last2 ;
}
Why have to check if last words are same so:
const confirmEnding = (str, target) => new RegExp(`${target}$`, '').test(str)
console.log(confirmEnding("Walking on water and developing software from a specification are easy if both are frozen", "specification"))
console.log(confirmEnding("Bastian", "n"))
console.log(confirmEnding("Connor", "n"))
console.log(confirmEnding("Walking on water and developing software from a specification are easy if both are frozen", "specification"))
console.log(confirmEnding("He has to give me a new name", "name"))
console.log(confirmEnding("Open sesame", "same"))
console.log(confirmEnding("Open sesame", "pen"))
console.log(confirmEnding("If you want to save our world, you must hurry. We dont know how much longer we can withstand the nothing", "mountain"))
Simplest way:
const confirmEnding = (_str, _target) => _str.charAt(_str.length - 1) === _target.charAt(_target.length - 1);
https://jsfiddle.net/pablodarde/hsdgjmzw/
Answer #Wiktor Stribiżew suggested:
function myValidate(word) {
return (word.length === 1 || /[^A-Z]/i.test(word)) ? true : false;
}
Hello during the creation of an array I have a function that will not allow words with certain characters etc to be added to the array
function myValidate(word) {
// No one letter words
if (word.length === 1) {
return true;
}
if (word.indexOf('^') > -1 || word.indexOf('$') > -1) {
return true;
}
return false;
}
It seems like not the proper way of going about this and ive been looking into a regex that would handle it but have not been successful implementing it, tried numerous efforts like:
if (word.match('/[^A-Za-z]+/g') ) {
return true;
}
can some one shed some light on the proper way of handling this?
I suggest using a simpler solution:
function myValidate(word) {
return (word.length === 1 || /[^A-Z]/i.test(word)) ? false : true;
}
var words = ["Fat", "Gnat", "x3-2741996", "1996", "user[50]", "definitions(edit)", "synopsis)"];
document.body.innerHTML = JSON.stringify(words.filter(x => myValidate(x)));
Where:
word.length === 1 checks for the string length
/[^A-Z]/i.test(word) checks if there is a non-ASCII-letter symbol in the string
If any of the above condition is met, the word is taken out of the array. The rest remains.
EDIT: using test instead of match
You want to use test() because it returns a bool telling you if you match the regex or not. The match(), instead, always returns the matched elements. Those may be cast to true by coercion. This is not what you want.
To sum it all up you can just use this one-liner (no if needed and no quotes either, cannot get any simpler):
return word.test(/^[a-zA-Z][a-zA-Z]+$/); // two letter words
You should whitelist characters instead of blacklisting. That's one of the principles in security. In your case, don't tell what is wrong, but tell what is right:
if (word.test('/^[a-zA-Z]+$/')) { // two letter words
return false;
}
This will return false for all words that contain ONLY [a-zA-Z] characters. I guess this is what you want.
Your regex, instead, looked for illegal characters by negating the character group with the leading ^.
Two recommendations:
Just use regex in a positive way (without negation) and it'll be a lot easier to understand.
Also, validation functions normally return true for good data and false for bad data.
It is more readable this way:
if (validate(data))
{
// that's some good data we have here!
}
I am checking an index Of string in JAVASCRIPT. and this is coming as false. where as the value does belong to it as below :
if(idOfControl.indexOf(idOfButton)) == is giving false for the below values.
idOfControl = "dlInventory_btnEditComment_0"
idOfButton = "dlInventory_btnEditComment"
But if I run idOfControl.replace(idOfButton, ""); It is working and replacing the text.
Any reason for this?
indexOf can also return 0, in the event of your string being found at the position 0. 0 evaluates to false. Try:
if(idOfControl.indexOf(idOfButton) > -1)
More info: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/indexOf
There are these three big options:
indexOf > -1
The result of indexOf can be 0 meaning that the string was found at the beginning of the string. When string is not found, the return value is -1, therefore:
if (idOfControl.indexOf(idOfButton) > -1) {
// Do something
}
Which can be nicer written as #paxdiablo commented:
if (idOfControl.indexOf(idOfButton) >= 0) {
// Do something
}
via regex
You can use a very simple regular expression to test your match.
var idOfControl = "dlInventory_btnEditComment_0"
var control = /dlInventory_btnEditComment/;
if (idOfControl.test(control)) {
// do something
}
This approach can be enhanced to capture the last number of your string (if you need it)
var idOfControl = "dlInventory_btnEditComment_0"
var control = /dlInventory_btnEditComment_(\d+)/;
var match = control.exec(idOfControl);
if (match) {
alert('the number found is: ' + match[1]);
}
You can try it out here: http://jsfiddle.net/4Z9UC/
via indexOf in a hacky way
This uses a bitwise operator to return a truthy value when the position is !=-1 (In two's complement notation, -1 is internally represented as 111...111, and its inversion is 000...000 which is 0, i.e. a falsy value). It is in fact more efficient than the >-1 option, but it is harder to read and to understand. (EDIT: this became so popular that you can say it is a standard)
if (~idOfControl.indexOf(idOfButton)) {
// do something
}
In javascript:
"Id".localeCompare("id")
will report that "id" is bigger. I want to do ordinal (not locale) compare such that "Id" is bigger. This is similar to String.CompareOrdinal in C#. How can I do it?
I support the answers given by Raymond Chen and pst. I will back them up with documentation from my favorite site for answers to JavaScript questions -- The Mozilla Developer Network. As an aside, I would highly recommend this site for any future JavaScript questions you may have.
Now, if you go to the MDN section entitled String, under the section "Comparing strings", you will find this description:
C developers have the strcmp() function for comparing strings. In JavaScript, you just use the less-than and greater-than operators:
var a = "a";
var b = "b";
if (a < b) // true
print(a + " is less than " + b);
else if (a > b)
print(a + " is greater than " + b);
else
print(a + " and " + b + " are equal.");
A similar result can be achieved using the localeCompare method inherited by String instances.
If we were to use the string "Id" for a and "id" for b then we would get the following result:
"Id is less than id"
This is the same result that Yaron got earlier when using the localeCompare method. As noted in MDN, using the less-than and greater-than operators yields similar results as using localeCompare.
Therefore, the answer to Yaron's question is to use the less-than (<) and greater-than (>) operators to do an ordinal comparison of strings in JavaScript.
Since Yaron mentioned the C# method String.CompareOrdinal, I would like to point out that this method produces exactly the same results as the above JavaScript. According to the MSDN C# documentation, the String.CompareOrdinal(String, String) method "Compares two specified String objects by evaluating the numeric values of the corresponding Char objects in each string." So the two String parameters are compared using the numeric (ASCII) values of the individual characters.
If we use the original example by Yaron Naveh in C#, we have:
int result = String.CompareOrdinal("Id", "id");
The value of result is an int that is less than zero, and is probably -32 because the difference between "I" (0x49) and "i" (0x69) is -0x20 = -32. So, lexically "Id" is less than "id", which is the same result we got earlier.
As Raymond noted (and explained) in a comment, an "ordinal" non-locale aware compare is as simple as using the various equality operators on strings (just make sure both operands are strings):
"a" > "b" // false
"b" > "a" // true
To get a little fancy (or don't muck with [[prototype]], the function is the same):
String.prototype.compare = function (a, b) {
return ((a == b ? 0)
? (a > b : 1)
: -1)
}
Then:
"a".compare("b") // -1
Happy coding.
Just a guess: by inverting case on all letters?
function compareOrdinal(ori,des){
for(var index=0;index<ori.length&&index<des.length;index++){
if(des[index].charCodeAt(0)<ori[index].charCodeAt(0)){
return -1;
break;
}
}
if(parseInt(index)===des.length-1){
return 0;
}
return 1;
}
compareOrdinal("idd","id");//output 1
if you need to compare and find difference between two string, please check this:
function findMissingString() {
var str1 = arguments[0];
var str2 = arguments[1];
var i = 0 ;
var j = 0 ;
var text = '' ;
while(i != (str1.length >= str2.length ? str1.length : str2.length )) {
if(str1.charAt(i) == str2.charAt(j)) {
i+=1 ;
j+=1;
} else {
var indexing = (str1.length >= str2.length ? str1.charAt(i) : str2.charAt(j));
text = text + indexing ;
i+=1;
j+=1;
}
}
console.log("From Text = " + text);
}
findMissingString("Hello","Hello world");
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)