Dima M
Dima M

Reputation: 43

How to sort an array by a specific key

I am using swapi.co as my source of json data and my response looks like the following: https://swapi.co/api/people/

My array of "characters" has the structure

// MARK: - CharactersResult
struct CharactersResult: Codable {
    let name, height, mass, hairColor: String
    let skinColor, eyeColor, birthYear: String
    let gender: Gender
    let homeworld: String
    let films, species, vehicles, starships: [String]
    let created, edited: String
    let url: String

    enum CodingKeys: String, CodingKey {
        case name, height, mass
        case hairColor = "hair_color"
        case skinColor = "skin_color"
        case eyeColor = "eye_color"
        case birthYear = "birth_year"
        case gender, homeworld, films, species, vehicles, starships, created, edited, url
    }
}

I would like to get the smallest character and the largest out of my array. My function is:

func getCharacters(urlToGet: String){
            do{
                if let url = URL(string: urlToGet) {
                    URLSession.shared.dataTask(with: url) { data, response, error in
                        if let data = data {
                            do {
                                let jsonCharacters = try JSONDecoder().decode(Characters.self, from: data)



                                self.characters = self.characters + jsonCharacters.results

                                self.nextPageUrlForCharacters = jsonCharacters.next

                                self.updatePicker()

                            } catch let error {
                                print(error)


                            }
                        }
                        }.resume()

                }
            }
        }

My main question is where to do the sorting and what is the most efficient way to get the smallest and the largest character.

Upvotes: 2

Views: 99

Answers (2)

vadian
vadian

Reputation: 285082

Assuming smallest and largest is related to the height of the character (then shortest and tallest are more appropriate), sort the array by that struct member. As the value is String you have to add the .numeric option.

The descending order starts with the largest value

let sortedCharacters = self.characters.sorted{$0.height.compare($1.height, options: .numeric) == .orderedDescending}
let tallestCharacter = sortedCharacters.first
let shortestCharacter = sortedCharacters.last

Side note: You can get rid of the CodingKeys if you add the convertFromSnakeCase key decoding strategy.

Upvotes: 3

Chris
Chris

Reputation: 3817

This simplified version of your code illustrates how to sort by an arbitrary key, as long as that key is Comparable (so I've made your height and mass properties both Ints rather than String):

struct MovieCharacter: Codable {
    let name: String
    let height: Int
    let mass: Int
}

let fred = MovieCharacter(name: "Fred", height: 123, mass: 99)
let wilma = MovieCharacter(name: "Wilma", height: 158, mass: 47)

let chars = [fred, wilma]

let heaviestToLightest = chars.sorted { $0.mass > $1.mass }
// Prints "Fred, Wilma"
print(heaviestToLightest.map { $0.name }.joined(separator: ", "))

let tallestToShortest = chars.sorted { $0.height > $1.height }
// prints "Wilma, Fred"
print(tallestToShortest.map { $0.name }.joined(separator: ", "))

To change the sort order, reverse the >' in the comparison closure. If you want to know the "most" and "least" of a particular order, use.firstand.last` on the result.

Also, to save yourself from having to maintain the CodingKeys enum, use .keyDecodingStrategy of a JSONDecoder:

let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let characters = try decoder.decode(Characters.self, from: jsonData)

Upvotes: 0

Related Questions