Сортировать разнородную коллекцию по типу и атрибуту в Swift - PullRequest
0 голосов
/ 08 февраля 2019

У меня есть гетерогенная коллекция разных типов, которые все соответствуют одному и тому же протоколу.Я хочу отсортировать массив по типу, а затем атрибут имени.Например, я хочу, чтобы этот массив животных сортировался по типу в следующем порядке: собака, птица, а затем рыба, а если они одного типа, я хочу отсортировать по имени.Вот код:

import Foundation

protocol Animal {
    var name: String { get set }
}

class Dog: Animal {
    var name: String

    init(name: String) {
        self.name = name
    }
}

class Bird: Animal {
    var name: String

    init(name: String) {
        self.name = name
    }
}

class Fish: Animal {
    var name: String

    init(name: String) {
        self.name = name
    }
}

let dogA = Dog(name: "A")
let dogB = Dog(name: "B")
let birdA = Bird(name: "A")
let birdB = Bird(name: "B")
let fishA = Fish(name: "A")
let fishB = Fish(name: "B")

let animals: [Animal] = [fishB, fishA, birdB, birdA, dogB, dogA]

let sortedAnimals = animals.sorted { first, second -> Bool in
    if first is Dog && !(second is Dog) {
        return true
    } else if first is Dog && second is Dog {
        return first.name < second.name
    }

    if first is Bird && !(second is Bird) {
        return true
    } else if first is Bird && second is Bird {
        return first.name < second.name
    }

    if first is Fish && !(second is Fish) {
        return true
    } else if first is Fish && second is Fish {
        return first.name < second.name
    }

    return first.name < second.name
}

sortedAnimals

Это работает и приводит к правильному порядку сортировки:

{name "A", type "Dog"}
{name "B", type "Dog"}
{name "A", type "Bird"}
{name "B", type "Bird"}
{name "A", type "Fish"}
{name "B", type "Fish"}

Но так как в рабочем коде у меня более 30 различных типов в коллекции такого родаповторения кажется очень дублирующим.Как я могу сделать такой вид без такого большого количества повторяющегося кода?

Ответы [ 2 ]

0 голосов
/ 08 февраля 2019

Если вы хотите отсортировать гетерогенную группу Animal объектов по свойству type, тогда это свойство должно быть в протоколе.Как только вы сделаете это изменение, это станет тривиальным.

0 голосов
/ 08 февраля 2019

Используйте [Animal.Type], чтобы установить порядок, а затем сначала сравните типы на равенство, чтобы решить, нужно ли вам сортировать по name или type.

let order: [Animal.Type] = [Dog.self, Bird.self, Fish.self]

let sortedAnimals = animals.sorted { first, second -> Bool in
    let firstIndex = order.firstIndex { $0 == type(of: first) } ?? Int.max
    let secondIndex = order.firstIndex { $0 == type(of: second) } ?? Int.max

    if firstIndex == secondIndex {
        return first.name < second.name
    } else {
        return firstIndex < secondIndex
    }
}

Примечания:

  1. Как написано, отсутствующие типы будут отсортированы в конец массива по name.
  2. Возможно, вы захотите добавить:

    assert(firstIndex != Int.max, "missing type \(type(of: first)) from order array")
    assert(secondIndex != Int.max, "missing type \(type(of: second)) from order array")
    

    , чтобы перехватывать типы, отсутствующие в массиве order.Хотя вы можете просто принудительно развернуть результат firstIndex(where:), assert предоставляет возможность найти пропущенные типы в Отладочных сборках, но исчезает в Release сборках.

  3. Сравнение кортежей (как объясняется @Hamish с этим ответом ) может использоваться для замены приведенного выше оператора if на:

    return (firstIndex, first.name) < (secondIndex, second.name)
    

    Спасибодля напоминания, @MartinR!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...