2014-11-24 6 views
2

Я хотел бы написать расширение для Array, которое безопасно возвращает развернутую версию.Unwrap Sparse Array in Swift

я могу сделать это с общим способом, как так:

func unwrapElements<T>(array: [T?]) -> [T] { 
    let filtered: [T?] = array.filter{ $0 != nil } 
    let unwrapped: [T] = filtered.map { $0! } 
    return unwrapped 
} 

И я могу назвать это так:

let sparseNames: [String?] = ["alice", "bob", nil, "doug", nil, nil, "george", "hubert"] 
let names: [String] = unwrapElements(sparseNames) 

где names заканчивается время ["alice", "bob", "doug", "george", "hubert"] и безопасно итерацию и работа с каждым элементом.

Однако, я хочу назвать это так:

let names = sparseNames.unwrapElements() 

Я видел несколько подобных вопросов (like this one), но они не решают, как создать метод в качестве расширения.


(это помечены Xcode6.1, чтобы обозначить версию Swift я использую)


Примечание: Swift 1.2 Beta 3 ввел функцию, которая помогает flatMap с массивами, необязательно Chaining , См. Это отлично blog post here

ответ

3

Вы не можете сделать это прямо сейчас в Swift. Чтобы добавить эту функцию в качестве расширения к Array, вам нужно как-то пометить, что она может быть вызвана только с некоторыми типами массивов: те, у которых есть дополнительные значения в качестве подтипа. К сожалению, вы не можете специализироваться на родовом типе, поэтому глобальные функции - единственный способ.

Это та же самая причина Array имеет sort метод, который принимает функцию сравнения в качестве параметра, но это не имеет sort, что «просто работает», если массив полон Comparable членов - чтобы получить что-то функция, вы должны смотреть на то верхнего уровня:

func sort<T : Comparable>(inout array: [T]) 
+0

well boo. Этот [вопрос] (http://stackoverflow.com/questions/25064644/how-to-determine-if-a-generic-is-an-optional-in-swift), казалось, указывал, что может быть способ сделать это , –

1

вы пробовали использовать filter и map для этого?

let array: [String?] = ["Hello", nil, "World"] 

let unwrapped = array.map{$0 ?? nil}.filter{$0 != nil}.map{$0!} 

println("unwrapped: \(unwrapped)") 
// prints "unwrapped: [Hello, World]" 

Первый map использует Nil коалесцирующего оператор разворачивать, если это возможно. Хотя, я возвращаю nil, так как следующий фильтр удаляет все значения nil. Последнее map действительно разворачивается.

+0

Итак, это интересно, если вы выполняете каждый из этих шагов в отдельных строках, 'Optional()' никогда не удаляется. вам нужно добавить 'as! [String] 'после отфильтрованной строки, что должно подразумеваться в последнем' .map'? –

+0

Вы имеете в виду отдельные инструкции 'let x ='? Я пробовал это сам и не должен был ничего менять: 'let step1 = array.map {$ 0 ??nil} // prints "[Необязательный (" Hello "), nil, Необязательный (" Мир ")]" ' ' let step2 = step1.filter {$ 0! = nil} // печатает "[Необязательно (" Привет "), «Необязательно» («Мир»)] «' 'let step3 = step2.map {$ 0!} // выводит [Hello, World]' –

+0

ах, при написании его я пропустил окончательный '$ 0!' –

0

Вы можете сделать это. Вот так:

extension Array { 
    func catOptionals<A>() -> [A] where Element == A? { 
     return self.flatMap{ $0 } 
    } 
}