2016-07-31 17 views
7

Mam słownik [String:String]. Chciałbym uzyskać wartość skojarzoną z dowolnym kluczem zawierającym ciąg "S". Zamówienie nie ma znaczenia.Znajdź klucz zawierający ciąg w słowniku swift

To nie jest łatwe: po prostu uzyskaj wszystkie klucze, wykonaj iterację, zwróć pierwszą spełniającą warunek.

JEDNAKŻE, chciałbym to zrobić, używając eleganckiego podejścia typu swift. Coś używającego funkcji filter lub map. I to gdzie ja zgubić ...

Odpowiedz

9

Ponieważ jesteś zainteresowany tylko w dowolna wartość dopasowywania, można użyć metoda indexOf() znajdowania pierwszego pasującego wpisu słownikowego. Działa to, ponieważ słownik jest zbiorem par klucz/wartość.

Swift 2:

let dict = ["foo": "bar", "PQRS": "baz"] 
let searchTerm = "S" 

if let index = dict.indexOf({ (key, _) in key.containsString(searchTerm) }) { 
    let value = dict[index].1 
    print(value) 
} else { 
    print("no match") 
} 

Jak tylko klucz zostanie znaleziony, predykat zwraca true i wyliczenie zatrzyma. index to "indeks słownika", który można użyć bezpośrednio, aby uzyskać odpowiedni wpis słownika.

dla przypadku niewrażliwe poszukiwaniu klucza, wymienić orzeczenie przez

{ 
    (key, _) in key.rangeOfString(searchTerm, options: .CaseInsensitiveSearch) != nil 
} 

W Swift 3 można użyć first(where:) aby znaleźć pierwsze dopasowanie elementu, to zapisuje jeden słownik odnośnika:

if let entry = dict.first(where: { (key, _) in key.contains(searchTerm) }) { 
    print(entry.value) 
} else { 
    print("no match") 
} 

W przypadku wyszukiwania klucza niewrażliwego na wielkość liter, zastąp predykat przez

{ 
    (key, _) in key.range(of: searchTerm, options: .caseInsensitive) != nil 
} 
+0

jeśli nie potrzebuję rozróżniania wielkości liter, ponieważ wiem, że ciągi znaków są pełne, czy mogę użyć key.contains() zamiast key.range() czy key.range() szybciej? – Adeline

+0

@Adeline: Nie sądzę, że to robi różnicę, ale 'key.contains()' jest prostsze, więc użyłbym tego. –

+0

Wszystkie trzy odpowiedzi są ważne, ale jego jedna jest najszybsza. – Adeline

4

Można użyć filter, contains i first znaleźć "S":

Swift 2

if let key = yourDictionary.keys.filter({ $0.lowercaseString.characters.contains("s") }).first, let result = yourDictionary[key] { 
    print(result) 
} 

Swift 3

if let key = yourDictionary.keys.filter({ $0.lowercased().contains("s") }).first, let result = yourDictionary[key] { 
    print(result) 
} 

W komentarzu, @Hamish oferty to doskonała alternatywa dla Swift 3: zamiast

filter({ ... }).first 

można użyć

first(where: { ... }) 

Przykład:

if let key = yourDictionary.keys.first(where: { $0.lowercased().contains("s") }), let result = yourDictionary[key] { 
    print(result) 
} 
+1

Należy zauważyć, że w Swift 3 można również użyć polecenia ['first (where:)'] (http://swiftdoc.org/v3.0/protocol/Sequence/#func-first_). – Hamish

+0

@Hamish To jest świetny pomysł, dzięki. Czy użyjesz go w odpowiedzi, czy też dodaję to do mojego (z atrybucją oczywiście)? – Moritz

+0

@EricD Nie mam nic przeciwko :) Zauważam, że Martin również użył go w swojej odpowiedzi (straszne wyczucie czasu tam!), Więc naprawdę nie widzę sensu w tym, żebym osobiście na to odpowiedział. – Hamish

4

Można to zrobić z flatMap i containsString:

Swift 2.x:

let dict = ["one": "1", "two": "2", "three": "3", "four": "4", "five": "5", "six": "6"] 

let results = dict.flatMap { (key, value) in key.lowercaseString.containsString("o") ? value : nil } 

print(results) 

wyjściowa:

["4", "1", "2"] 
print(results.first ?? "Not found") 
4 

Albo jeśli kochasz jedno tajemnicze wkładki:

let first = dict.flatMap { $0.lowercaseString.containsString("o") ? $1 : nil }.first ?? "Not found" 

Dla Swift 3:

let dict = ["one": "1", "two": "2", "three": "3", "four": "4", "five": "5", "six": "6"] 

let results = dict.flatMap { (key, value) in key.lowercased().contains("o") ? value : nil } 

print(results) 
print(results.first ?? "Not Found") 

Albo jeśli kochasz jedno tajemnicze wkładki:

let first = dict.flatMap { $0.lowercased().contains("o") ? $1 : nil }.first ?? "Not Found"