2

I want to sort an array of strings so that alphabetic characters are always before any other kinds of characters. For example:

["800", "word", "test"]

Should sort to:

["test", "word", "800"]

The alphabetic strings can have numbers in them, but they can't have them as the first letter. So for example, if it's "ab8s" that should still come before "dog".

How would I do this? The comparison operators in Swift by default sort numbers before letters.

Doug Smith
  • 29,668
  • 57
  • 204
  • 388
  • Should `ab8s` come before or after `abcs`? I assuming you mean it should be after (so numbers are always greater than numbers) – Rob Napier Nov 03 '15 at 22:18

3 Answers3

2

How about this.

func sortedNumbersLast(words: [String]) -> [String] {
  var startsWithDigit     = [String]()
  var startsWithCharacter = [String]()

  for word in words {
    if let first = word.characters.first {
      if first >= "0" && first <= "9" {
        startsWithDigit.append(word)
      }
      else {
        startsWithCharacter.append(word)
      }
    }
  }
  return startsWithCharacter.sort(<) + startsWithDigit.sort(<)
}

enter image description here

Price Ringo
  • 3,424
  • 1
  • 19
  • 33
2

The key is to write your "is ordered before" function to do whatever you want. For example, if by digits, you mean "0"..."9", then something like this is probably what you want:

func isDigit(c: Character) -> Bool {
    return "0" <= c && c <= "9"
}

func sortedLettersFirst(lhs: String, rhs: String) -> Bool {
    for (lc, rc) in zip(lhs.characters, rhs.characters) {
        if lc == rc { continue }

        if isDigit(lc) && !isDigit(rc) {
            return false
        }
        if !isDigit(lc) && isDigit(rc) {
            return true
        }
        return lc < rc
    }
    return lhs.characters.count < rhs.characters.count
}

words.sort(sortedLettersFirst)

Of course, if by "digit" you mean "unicode digits", then see What is the replacement for isDigit() for characters in Swift? for a different approach to isDigit. But ultimately, the point is to make whatever rule you want in your isOrderedBefore function, and pass that to sort().

Community
  • 1
  • 1
Rob Napier
  • 286,113
  • 34
  • 456
  • 610
0

Try this in Playground, as you can see in the console output it gets the job done, letters are being placed at the front, followed by numbers and other symbols are at the end.

   let stringsSortedByLettersFirstNumbersThenAndOtherSymbolsAtTheEnd: (String, String) -> Bool = { s1, s2 -> Bool in
        guard let f1 = s1.first, let f2 = s2.first else {
            return s1 < s2
        }
        if f1.isLetter == false && f2.isLetter {
            return false
        }
        if f1.isLetter && f2.isLetter == false {
            return true
        }
        if f1.isNumber == false && f2.isNumber {
            return false
        }
        if f1.isNumber && f2.isNumber == false {
            return true
        }
        return s1 < s2
    }

    let str = ["4", "A", "B", "Z", "T", "3", "'", "8"]

    print(str.sorted(by: stringsSortedByLettersFirstNumbersThenAndOtherSymbolsAtTheEnd))

Console output

Above method sorts String in ascending order, if you want to do the other way just change the lines

        return s1 < s2

to

        return s1 > s2
Adam
  • 1,776
  • 1
  • 17
  • 28