Determinando se o dicionário Swift contém chave e obtendo qualquer um de seus valores

260

Atualmente, estou usando os seguintes (desajeitados) trechos de código para determinar se um dicionário Swift (não vazio) contém uma determinada chave e para obter um (qualquer) valor do mesmo dicionário.

Como se pode colocar isso de maneira mais elegante no Swift?

// excerpt from method that determines if dict contains key
if let _ = dict[key] {
    return true
}
else {
    return false
}

// excerpt from method that obtains first value from dict
for (_, value) in dict {
    return value
}
Drux
fonte
Você pode usar indexForKeyse achar que é mais claro e mais explícito; stackoverflow.com/a/29299943/294884
Fattie
Muitas vezes, o que você deseja é basicamente: cityName:String = dict["city"] ?? "" O ?? ""aqui significa basicamente "se não houver essa chave, retorne um espaço em branco".
Fattie

Respostas:

481

Você não precisa de nenhum código especial para fazer isso, porque é o que um dicionário já faz. Quando você buscar dict[key]você sabe se o dicionário contém a chave, porque o opcional que você recebe de volta não é nil(e contém o valor).

Portanto, se você quiser apenas responder à pergunta se o dicionário contém a chave, pergunte:

let keyExists = dict[key] != nil

Se você deseja o valor e sabe que o dicionário contém a chave, diga:

let val = dict[key]!

Mas se, como geralmente acontece, você não sabe que ela contém a chave - você deseja buscá-la e usá-la, mas apenas se existir -, use algo como if let:

if let val = dict[key] {
    // now val is not nil and the Optional has been unwrapped, so use it
}
mate
fonte
6
Eu não sigo. Acabei de obter um valor (duas vezes). O que mais você quer?
matt
Não existe "o primeiro valor"; um dicionário não é ordenado. Se você quer apenas os valores, sem as chaves, digamos dict.values.
Matt
1
Tenha cuidado, porque dict.valuesé opaco. Você pode andar de bicicleta, mas é tudo. (Ok, não é tudo, mas me humor.) Se você quiser que seja reificado para uma matriz, aceite dict.values.array.
Matt
1
@bubakazouba Experimente o seguinte: let d = ["hey":"ho"]; let s = d["hey"]; print(s)É um opcional, como sempre foi.
mate
1
@ Kross Essa é uma pergunta muito profunda, mas, portanto, faça-a separadamente.
matt
68

Por que não simplesmente procurar dict.keys.contains(key)? A verificação dict[key] != nilnão funcionará nos casos em que o valor for nulo. Como em um dicionário, [String: String?]por exemplo.

Hans Terje Bakke
fonte
2
Ou, em vez de apenas escrever, if let val = dict[key]você pode usar if let val = dict[key] as? Stringneste caso.
Okhan Okbay
.keys.contains não ajuda a distinguir se a chave existe ou se o valor é nulo. Talvez em versões rápidas anteriores? Não funciona para mim em rápida 4.
Rowan Gontier
8
Isso é potencialmente muito inútil, porque (1) você chama dict.keysque cria uma matriz com todas as chaves e, em seguida, (2) verifica todas as chaves em ordem até encontrar uma (ou nenhuma!). Eu sei que você está tentando resolver o caso de um [String: String?], mas seria muito cuidado com essa solução ...
charles
3
Como o @charles mencionou, isso eliminaria o benefício de pesquisa rápida que o uso de um dicionário oferece, então eu desaconselharia esse, mas ainda definitivamente uma opção válida.
que você
4
Você nunca deve usar esse método, pois possui complexidade O (n) em vez de teórico (depende da implementação da função hash) O (1) do acesso direto ao dicionário.
Krypt
45

A resposta aceita let keyExists = dict[key] != nilnão funcionará se o Dicionário contiver a chave, mas tiver um valor nulo.

Se você quiser ter certeza de que o Dicionário não contém a chave, use isso (testado no Swift 4).

if dict.keys.contains(key) {
  // contains key
} else { 
  // does not contain key
}
bergy
fonte
3
É o mesmo que a resposta de Han.
Declan McKenna
1
A verificação == nilfunciona, mesmo se o dicionário tiver valores opcionais. Isso ocorre porque o resultado da pesquisa é agrupado como um Optional. Por outro lado, para verificar se o valor pesquisado é realmente, nile não ausente, você pode usar == .some(nil).
jedwidz
1
Eu acrescentaria a eficiência do uso desse método em comparação com a pesquisa O (1) dos outros métodos. Eu acredito que é O (n) lookup
Alberto Vega
1
@ AlbertoVega-MSFT É O(1). Veja também: github.com/apple/swift-evolution/blob/master/proposals/… O que fez você pensar que era O(n)?
Alexander - Restabelece Monica
1
A documentação para a Dictionary.Keys.contains(...)função aqui diz que tem O(n)complexidade, onde nestá o comprimento da sequência.
Adil Hussain
5

Parece que você obteve o que precisa em @matt, mas se desejar uma maneira rápida de obter um valor para uma chave ou apenas o primeiro valor, se essa chave não existir:

extension Dictionary {
    func keyedOrFirstValue(key: Key) -> Value? {
        // if key not found, replace the nil with 
        // the first element of the values collection
        return self[key] ?? first(self.values)
        // note, this is still an optional (because the
        // dictionary could be empty)
    }
}

let d = ["one":"red", "two":"blue"]

d.keyedOrFirstValue("one")  // {Some "red"}
d.keyedOrFirstValue("two")  // {Some "blue"}
d.keyedOrFirstValue("three")  // {Some "red”}

Observe que não há garantia do que você realmente obterá como o primeiro valor; neste caso, acontece apenas para retornar "vermelho".

Velocidade da velocidade do ar
fonte
1
o bom uso do ??operador tirou de mim minha solução de conveniência, mas como uma extensão desse valor padrão, poderia apresentar insegurança nos dados ou comportamentos inesperados. Isso soa como algo que uma subclasse concreta Dictionarydeveria empregar. Com que frequência você precisa do primeiro valor em caso de nilretorno, excluindo a funcionalidade específica da situação?
NoodleOfDeath 15/04
1
if dictionayTemp["quantity"] != nil
    {

  //write your code
    }
Paresh Hirpara
fonte
0

Minha solução para uma implementação de cache que armazena NSAttributedString opcional:

public static var attributedMessageTextCache    = [String: NSAttributedString?]()

    if attributedMessageTextCache.index(forKey: "key") != nil
    {
        if let attributedMessageText = TextChatCache.attributedMessageTextCache["key"]
        {
            return attributedMessageText
        }
        return nil
    }

    TextChatCache.attributedMessageTextCache["key"] = .some(.none)
    return nil
Rishi
fonte
0

Aqui está o que funciona para mim no Swift 3

let _ = (dict[key].map { $0 as? String } ?? "")
ΩlostA
fonte