1

I'm using a for loop to create a string of a dicts keys and values. Unfortunately swift behaves differently on Mac and Linux.

for key in parameters.keys.sorted() {...}

I want to sort my keys by byte value, not alphabetically, lowercase parameters should be listed after uppercase ones.

So a key like "AWT" should come before a key like "Ast".

Leo
  • 380
  • 1
  • 17
  • 1
    On Apple platforms you'll get the desired order. On Linux that is a known bug which should be fixed in Swift 4, see https://bugs.swift.org/browse/SR-530. – Note that this has nothing to do with dictionaries or keys, but only how the comparison of strings is implemented, `print(["AWT", "Ast"].sorted())` is sufficient to see the difference between Apple and Linux. – Martin R May 11 '17 at 17:06
  • .sorted() can take parameters "sorted using the given predicate as the comparison between elements". Is there a way to manually achieve the right sorting? – Leo May 11 '17 at 17:44
  • Perhaps you should clarify first what "sort by byte value" means for you exactly. – Martin R May 11 '17 at 17:47
  • okay, for me it means that when looked up: http://www.asciitable.com/ so the decimal value of a uppercase "W" comes before a lowercase "a". So like in the ascii table, i would like to sort it. Compared to my example, with "AWT" and "Ast" both start with an "A" so equal, but then "W" comes before "a". – Leo May 11 '17 at 18:02
  • What about non-ASCII characters? On Apple platforms, "a" < "ä" < "b", "e" < "é" < "f". – Martin R May 11 '17 at 18:04
  • non-ASCII characters won't occur here – Leo May 11 '17 at 18:17

1 Answers1

3

On Apple platforms, Swift strings comparison is a lexicographical comparison of Unicode scalar values, based on the so-called "Unicode Normalization Form D", see How String Comparison happens in Swift or What does it mean that string and character comparisons in Swift are not locale-sensitive? for details.

On Linux, the sort order is different. That is a known problem ([String] sort order varies on Darwin vs. Linux) and should be fixed in Swift 4.

If you only care about ASCII characters then a possible approach would be to compare the UTF-8 representation of the strings:

func utf8StringCompare(s1: String, s2: String) -> Bool {
    let u1 = s1.utf8
    let u2 = s2.utf8
    for (x, y) in zip(u1, u2) {
        if x < y { return true }
        if x > y { return false }
    }
    return u1.count < u2.count
}


let s = ["AWT", "Ast"].sorted(by: utf8StringCompare)
print(s) // ["AWT", "Ast"]

This gives identical results on Apple platforms and on Linux.

But note that this is not the default sort order for Swift Strings on Apple platforms. To replicate that on Linux (before it is fixed in Swift 4), the following algorithm would work:

func unicodeStringCompare(s1: String, s2: String) -> Bool {
    let u1 = s1.decomposedStringWithCanonicalMapping.unicodeScalars
    let u2 = s2.decomposedStringWithCanonicalMapping.unicodeScalars
    for (x, y) in zip(u1, u2) {
        if x.value < y.value { return true }
        if x.value > y.value { return false }
    }
    return u1.count < u2.count
}

let someStrings = ["a", "b", "e", "f", "ä", "é"].sorted(by: unicodeStringCompare)
print(someStrings) // ["a", "ä", "b", "e", "é", "f"]
Community
  • 1
  • 1
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382