2016-12-21 2 views
3

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

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

примера

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 
    } 
} 

Но что дает войну ning: Двоичный оператор не может применяться к операндам типа «Элемент» и «Т».

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

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] 

Справка была бы принята с благодарностью.

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

ответ

4

Вы определили общий метод

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(), но это не очень эффективно, поскольку промежуточные массивы создаются в каждой итерации:

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' 
+1

Спасибо! Отлично. – MH175

Смежные вопросы