Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift sort an array with strings and numbers [duplicate]

I have an array of strings,

let array = [ "10", "1", "101", "NA", "100", "20", "210", "200", "NA", "7" ] 

I would like to get the output sorted in ascending as,

let sorted = [ "1", "7", "10", "20", "100", "101", "200", "210", "NA", "NA" ] 

I tried using the sorted command but it does not work when it encounters more than 2 digits e.g.: 100, 101, 200 etc.

array.sorted { $0? < $1? } 

What would be the simple way to get this?

like image 593
dacscan3669 Avatar asked May 09 '17 12:05

dacscan3669


1 Answers

edit/update: Xcode 11.3 • Swift 5.2 or later

You can use String method localizedStandardCompare (diacritics and case insensitive):

let array = [ "10", "1", "101", "NA", "100", "20", "210", "200", "NA", "7" ] let sorted = array.sorted {$0.localizedStandardCompare($1) == .orderedAscending}  print(sorted) // ["1", "7", "10", "20", "100", "101", "200", "210", "NA", "NA"] 

or using the method sort(by:) on a MutableCollection:

var array = [ "10", "1", "101", "NA", "100", "20", "210", "200", "NA", "7" ] array.sort {$0.localizedStandardCompare($1) == .orderedAscending}  print(array) // ["1", "7", "10", "20", "100", "101", "200", "210", "NA", "NA"] 

You can also implement your own localized standard sort method extending Collection:

public extension Sequence where Element: StringProtocol {      func localizedStandardSorted(ascending: Bool = true) -> [Element] {         let result: ComparisonResult = ascending ? .orderedAscending : .orderedDescending         return sorted { $0.localizedStandardCompare($1) == result }     } } 

let array = [ "10", "1", "101", "NA", "100", "20", "210", "200", "NA", "7" ] let sorted = array.localizedStandardSorted()  print(sorted) // ["1", "7", "10", "20", "100", "101", "200", "210", "NA", "NA"] 

The mutating method as well extending MutableCollection:

public extension MutableCollection where Element: StringProtocol, Self: RandomAccessCollection {     mutating func localizedStandardSort(ascending: Bool = true) {         let result: ComparisonResult = ascending ? .orderedAscending : .orderedDescending         return sort { $0.localizedStandardCompare($1) == result }     } } 

var array = [ "10", "1", "101", "NA", "100", "20", "210", "200", "NA", "7" ] array.localizedStandardSort()  print(array) // ["1", "7", "10", "20", "100", "101", "200", "210", "NA", "NA"] 

If you need to sort your array numerically you can use String compare method setting the options parameter to .numeric:

public extension Sequence where Element: StringProtocol {     func sortedNumerically(ascending: Bool = true) -> [Element] {         let result: ComparisonResult = ascending ? .orderedAscending : .orderedDescending         return sorted { $0.compare($1, options: .numeric) == result }     } } 

public extension MutableCollection where Element: StringProtocol, Self: RandomAccessCollection {     mutating func sortNumerically(ascending: Bool = true) {         let result: ComparisonResult = ascending ? .orderedAscending : .orderedDescending         return sort { $0.compare($1, options: .numeric) == result }     } } 

var numbers = ["1.5","0.5","1"] let sortedNumbers = numbers.sortedNumerically() print(sortedNumbers)  // ["0.5", "1", "1.5"] print(numbers) // ["1.5","0.5","1"] // mutating the original collection numbers.sortNumerically(ascending: false) print(numbers)  // "["1.5", "1", "0.5"]\n" 


To sort a custom class/structure by one of its properties:


extension MutableCollection where Self: RandomAccessCollection {     public mutating func localizedStandardSort<T: StringProtocol>(_ predicate: (Element) -> T, ascending: Bool = true) {         sort {             predicate($0).localizedStandardCompare(predicate($1)) ==                 (ascending ? .orderedAscending : .orderedDescending)         }     } } 

public extension Sequence {     func localizedStandardSorted<T: StringProtocol>(_ predicate: (Element) -> T, ascending: Bool = true) -> [Element] {         sorted {             predicate($0).localizedStandardCompare(predicate($1)) ==                 (ascending ? .orderedAscending : .orderedDescending)         }     } } 

public extension Sequence {     func sortedNumerically<T: StringProtocol>(_ predicate: (Element) -> T, ascending: Bool = true) -> [Element] {         let result: ComparisonResult = ascending ? .orderedAscending : .orderedDescending         return sorted { predicate($0).compare(predicate($1), options: .numeric) == result }     } } 

public extension MutableCollection where Element: StringProtocol, Self: RandomAccessCollection {     mutating func sortNumerically<T: StringProtocol>(_ predicate: (Element) -> T, ascending: Bool = true) {         let result: ComparisonResult = ascending ? .orderedAscending : .orderedDescending         return sort { predicate($0).compare(predicate($1), options: .numeric) == result }     } } 


Playground testing

struct Person {     let name: String     let age : Int }  extension Person : CustomStringConvertible {     var description: String { "name: \(name), age: \(age)" } } 

let people: [Person] = [.init(name: "Éd Sheeran", age: 26),                         .init(name: "phil Collins", age: 66),                         .init(name: "Shakira", age: 40),                         .init(name: "rihanna", age: 25),                         .init(name: "Bono", age: 57)]  let sorted = people.localizedStandardSorted(\.name)  print(sorted) // [name: Bono, age: 57, name: Éd Sheeran, age: 26, name: phil Collins, age: 66, name: rihanna, age: 25, name: Shakira, age: 40] 
like image 65
Leo Dabus Avatar answered Sep 20 '22 12:09

Leo Dabus