Swift универсальная функция массива, чтобы найти все индексы элементов, не соответствующих элементу

Свифт 3

Попытка написать общее расширение массива, которое получает все индексы элементов, которые не равны значению

пример

 let arr: [String] = ["Empty", "Empty", "Full", "Empty", "Full"]
 let result: [Int] = arr.indexes(ofItemsNotEqualTo item: "Empty")
 //returns [2, 4]

Я попытался сделать общую функцию:

extension Array {

    func indexes<T: Equatable>(ofItemsNotEqualTo item: T) -> [Int]?  {
        var result: [Int] = []
        for (n, elem) in self.enumerated() {
            if elem  != item {
                result.append(n)
            }
        }
        return result.isEmpty ? nil : result
    }
}

Но это дает предупреждение: бинарный оператор нельзя применять к операндам типа "Элемент" и "Т".

Итак, я сделал это, где я бросил элемент (обратите внимание, как?)

extension Array {

    func indexes<T: Equatable>(ofItemsNotEqualTo item: T) -> [Int]?  {
        var result: [Int] = []
        for (n, elem) in self.enumerated() {
            if elem as? T != item {
                result.append(n)
            }
        }
        return result.isEmpty ? nil : result
    }
}

Но теперь кажется, что проверка типов вышла за пределы окна, потому что, если я передам целое число, я получу неправильный результат

 let arr: [String] = ["Empty", "Empty", "Full", "Empty", "Full"]
 let result: [Int] = arr.indexes(ofItemsNotEqualTo item: 100)
 //returns [0, 1, 2, 3, 4]

Помощь будет принята с благодарностью.

Есть ли лучший способ сделать это с помощью функции Reduce?

2 ответа

Решение

Вы определили универсальный метод

func indexes<T: Equatable>(ofItemsNotEqualTo item: T) -> [Int]?

который принимает аргумент типа T который должен бытьEquatable, но не имеет отношения к Element Тип массива.

Следовательно

let arr = ["Empty", "Empty", "Full", "Empty", "Full"]
let result = arr.indexes(ofItemsNotEqualTo: 100)

компилируется, но elem as? T дает nil (который != item) для всех элементов массива.

То, что вы хотите, это метод, который определен только для массивовEquatable элементы. Это может быть достигнуто с ограниченным расширением:

extension Array where Element: Equatable {
    func indexes(ofItemsNotEqualTo item: Element) -> [Int]?  {
        var result: [Int] = []
        for (n, elem) in enumerated() {
            if elem != item {
                result.append(n)
            }
        }
        return result.isEmpty ? nil : result
    }
}

На самом деле я бы не сделал возвращаемое значение необязательным. Если все элементы равны данному элементу, то логическим возвращаемым значением будет пустой массив.

Есть ли лучший способ сделать это с помощью функции Reduce?

Ну, вы могли бы использовать reduce(), но это не очень эффективно, потому что промежуточные массивы создаются на каждом шаге итерации:

extension Array where Element: Equatable {
    func indexes(ofItemsNotEqualTo item: Element) -> [Int]  {
        return enumerated().reduce([]) {
            $1.element == item ? $0 : $0 + [$1.offset]
        }
    }
}

На самом деле у вас есть операция "фильтр + карта":

extension Array where Element: Equatable {
    func indexes(ofItemsNotEqualTo item: Element) -> [Int]  {
        return enumerated().filter { $0.element != item }.map { $0.offset }
    }
}

который может быть упрощен с помощью flatMap():

extension Array where Element: Equatable {

    func indexes(ofItemsNotEqualTo item: Element) -> [Int]  {
        return enumerated().flatMap { $0.element != item ? $0.offset : nil }
    }
}

Примеры:

let arr = ["Empty", "Empty", "Full", "Empty", "Full"]
arr.indexes(ofItemsNotEqualTo: "Full") // [0, 1, 3]

[1, 1, 1].indexes(ofItemsNotEqualTo: 1) // []

arr.indexes(ofItemsNotEqualTo: 100)
// error: cannot convert value of type 'Int' to expected argument type 'String'

вы можете использовать индексы в Swift 5

      let indexes = arr.indices.filter({ arr[$0] != "Empty"})
Другие вопросы по тегам