簡體   English   中英

如何對 JavaScript 中的字符串進行排序

[英]How to sort strings in JavaScript

我有一個對象列表,我希望根據字符串類型的字段attr進行排序。 我嘗試使用-

list.sort(function (a, b) {
    return a.attr - b.attr
})

但發現-似乎不適用於 JavaScript 中的字符串。 如何根據字符串類型的屬性對對象列表進行排序?

根據您的示例使用String.prototype.localeCompare

list.sort(function (a, b) {
    return ('' + a.attr).localeCompare(b.attr);
})

我們強制 a.attr 為字符串以避免異常。 從 Internet Explorer 6和 Firefox 1 開始就支持localeCompare 。您可能還會看到使用的以下代碼不尊重語言環境:

if (item1.attr < item2.attr)
  return -1;
if ( item1.attr > item2.attr)
  return 1;
return 0;

更新的答案(2014 年 10 月)

我對這個字符串自然排序順序感到非常惱火,所以我花了一些時間來調查這個問題。 我希望這有幫助。

長話短說

localeCompare()字符支持很糟糕,只需使用它。 正如Shog9所指出的,您的問題的答案是:

return item1.attr.localeCompare(item2.attr);

在所有自定義 javascript“自然字符串排序順序”實現中發現的錯誤

那里有很多自定義實現,試圖更精確地進行字符串比較,稱為“自然字符串排序順序”

在“玩”這些實現時,我總是注意到一些奇怪的“自然排序”選擇,或者更確切地說是錯誤(或者在最好的情況下是遺漏)。

通常,無法正確處理特殊字符(空格、破折號、與號、括號等)。

然后,您會發現它們在不同的地方混雜在一起,通常可能是:

  • 有些會在大寫“Z”和小寫“a”之間
  • 有些會在“9”和大寫“A”之間
  • 有些會在小寫“z”之后

當人們期望特殊字符全部“組合”在一個地方時,可能除了空格特殊字符(它始終是第一個字符)。 也就是說,要么全部在數字之前,要么全部在數字和字母之間(小寫和大寫一個接一個地“在一起”),或者全部在字母之后。

我的結論是,當我開始添加幾乎不常見的字符(即帶有變音符號或破折號、感嘆號等字符的字符)時,它們都無法提供一致的順序。

定制實現的研究:

瀏覽器通過localeCompare()實現的原生“自然字符串排序”實現

IE6+ 支持localeCompare()最舊的實現(沒有語言環境和選項參數),請參閱http://msdn.microsoft.com/en-us/library/ie/s4esdbwz ( localeCompare() .aspx (向下滾動到 localeCompare() 方法)。 內置的localeCompare()方法在排序方面做得更好,甚至是國際和特殊字符。 使用localeCompare()方法的唯一問題是“使用的語言環境和排序順序完全依賴於實現”。 換句話說,當使用諸如 stringOne.localeCompare(stringTwo) 之類的 localeCompare 時:Firefox、Safari、Chrome 和 IE 對字符串有不同的排序順序。

瀏覽器原生實現的研究:

“字符串自然排序順序”的難點

實現一個可靠的算法(意思是:一致但也涵蓋廣泛的字符)是一項非常艱巨的任務。 UTF8 包含超過 2000 個字符涵蓋超過 120 種腳本(語言) 最后,此任務有一些規范,稱為“Unicode 排序算法”,可在http://www.unicode.org/reports/tr10/找到。 您可以在我發布的這個問題上找到更多信息https://softwareengineering.stackexchange.com/questions/257286/is-there-any-language-agnostic-specification-for-string-natural-sorting-order

定論

因此,考慮到我遇到的 javascript 自定義實現提供的當前支持水平,我們可能永遠不會看到任何接近支持所有這些字符和腳本(語言)的東西。 因此,我寧願使用瀏覽器的本地 localeCompare() 方法。 是的,它確實具有跨瀏覽器不一致的缺點,但基本測試表明它涵蓋了更廣泛的字符,允許可靠且有意義的排序順序。

正如Shog9所指出的,您的問題的答案是:

return item1.attr.localeCompare(item2.attr);

進一步閱讀:

感謝 Shog9 的好回答,我相信這讓我朝着“正確”的方向前進

答案(在現代 ECMAScript 中)

list.sort((a, b) => (a.attr > b.attr) - (a.attr < b.attr))

或者

list.sort((a, b) => +(a.attr > b.attr) || -(a.attr < b.attr))

描述

將布爾值轉換為數字會產生以下結果:

  • true -> 1
  • false -> 0

考慮三種可能的模式:

  • x 大於 y: (x > y) - (y < x) -> 1 - 0 -> 1
  • x 等於 y: (x > y) - (y < x) -> 0 - 0 -> 0
  • x 小於 y: (x > y) - (y < x) -> 0 - 1 -> -1

(選擇)

  • x 大於 y: +(x > y) || -(x < y) +(x > y) || -(x < y) -> 1 || 0 1 || 0 -> 1
  • x 等於 y: +(x > y) || -(x < y) +(x > y) || -(x < y) -> 0 || 0 0 || 0 -> 0
  • x 小於 y: +(x > y) || -(x < y) +(x > y) || -(x < y) -> 0 || -1 0 || -1 -> -1

所以這些邏輯等價於典型的排序比較器函數。

if (x == y) {
    return 0;
}
return x > y ? 1 : -1;

由於字符串可以直接在 javascript 中進行比較,這將完成這項工作

list.sort(function (a, b) {
    return a.attr > b.attr ? 1: -1;
})

排序函數中的減法僅在需要非字母(數字)排序時使用,當然它不適用於字符串

您應該在此處使用 > 或 < 和 ==。 所以解決方案是:

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

嵌套三元箭頭函數

(a,b) => (a < b ? -1 : a > b ? 1 : 0)

我為此困擾了很長時間,所以我終於研究了這個,並給了你這個冗長的理由來解釋為什么事情會這樣。

規格

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)

所以現在我們去 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.

而已。 如果參數是完全相同的字符串(相同的長度和對應位置的相同字符),應用於字符串的三重等號運算符返回 true。

因此,當我們嘗試比較可能來自不同來源但我們知道最終將具有相同值的字符串時, ===將起作用 - 這是我們代碼中內聯字符串的常見場景。 例如,如果我們有一個名為connection_state的變量,並且我們想知道它現在處於以下哪個狀態['connecting', 'connected', 'disconnecting', 'disconnected'] ,我們可以直接使用===

但還有更多。 就在 11.9.4 之上,有一個簡短的說明:

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.

唔。 現在怎么辦? 外部獲得的字符串可以而且很可能會是奇怪的 unicodey,而我們溫柔的===不會對它們進行公正處理。 進來localeCompare來救援:

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. 

我們現在可以回家了。

tl;博士;

要比較 javascript 中的字符串,請使用localeCompare 如果您知道字符串沒有非 ASCII 組件,因為它們是例如內部程序常量,那么===也可以工作。

解釋為什么問題中的方法不起作用:

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

字符串之間的減法返回 NaN。

與@Alejadro 的回答相呼應,正確的做法是——

products.sort( (a,b) => a.name > b.name ? 1 : -1 )

應該有升序和降序功能

if (order === 'asc') {
  return a.localeCompare(b);
}
return b.localeCompare(a);

在 javascript 中,我們使用.localCompare方法對字符串進行排序

對於打字稿

const data = ["jane", "mike", "salome", "ababus", "buisa", "dennis"];

const sort = (arr: string[], mode?: 'desc' | 'asc') => 
  !mode || mode === "asc"
    ? arr.sort((a, b) => a.localeCompare(b))
    : arr.sort((a, b) => b.localeCompare(a));


console.log(sort(data, 'desc'));// [ 'salome', 'mike', 'jane', 'dennis', 'buisa', 'ababus' ]
console.log(sort(data, 'asc')); // [ 'ababus', 'buisa', 'dennis', 'jane', 'mike', 'salome' ]


對於 JS

const data = ["jane", "mike", "salome", "ababus", "buisa", "dennis"];

/**
 * @param {string[]} arr
 * @param {"asc"|"desc"} mode
 */
const sort = (arr, mode = "asc") =>
  !mode || mode === "asc"
    ? arr.sort((a, b) => a.localeCompare(b))
    : arr.sort((a, b) => b.localeCompare(a));

console.log(sort(data, 'desc'));// [ 'salome', 'mike', 'jane', 'dennis', 'buisa', 'ababus' ]
console.log(sort(data, 'asc')); // [ 'ababus', 'buisa', 'dennis', 'jane', 'mike', 'salome' ]
list.sort(function(item1, item2){
    return +(item1.attr > item2.attr) || +(item1.attr === item2.attr) - 1;
}) 

他們如何工作示例:

+('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

在您最初問題的操作中,您正在執行以下操作:

item1.attr - item2.attr

因此,假設這些是數字(即 item1.attr = "1", item2.attr = "2"),只要您確保類型,您仍然可以使用“===”運算符(或其他嚴格的評估器)。 以下應該有效:

return parseInt(item1.attr) - parseInt(item2.attr);

如果它們是 alphaNumeric,則使用 localCompare()。

這不是在javascript中對字符串排序的方法! 標題應為:如何在JavaScript中對字符串數組進行排序。

直接使用 sort() 沒有任何-<

 const areas = ['hill', 'beach', 'desert', 'mountain'] console.log(areas.sort()) // To print in descending way console.log(areas.sort().reverse())

如果要控制語言環境(或大小寫或重音),請使用Intl.collator<\/code><\/a> :

const collator = new Intl.Collator();
list.sort((a, b) => collator.compare(a.attr, b.attr));
<!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)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM