Determinar si el diccionario Swift contiene clave y obtener alguno de sus valores

260

Actualmente estoy usando los siguientes fragmentos de código (torpes) para determinar si un diccionario Swift (no vacío) contiene una clave determinada y para obtener un (cualquier) valor del mismo diccionario.

¿Cómo se puede poner esto más elegantemente en 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
fuente
Puede usarlo indexForKeysi cree que es más claro y más explícito; stackoverflow.com/a/29299943/294884
Fattie
Muy a menudo, lo que quiere decir básicamente: cityName:String = dict["city"] ?? "" El ?? ""aquí significa básicamente "si no hay tal clave, devuelve un espacio en blanco".
Fattie

Respuestas:

481

No necesita ningún código especial para hacer esto, porque es lo que un diccionario ya hace. Cuando busca dict[key], sabe si el diccionario contiene la clave, porque el Opcional que obtiene no lo está nil(y contiene el valor).

Entonces, si solo desea responder la pregunta de si el diccionario contiene la clave, pregunte:

let keyExists = dict[key] != nil

Si desea el valor y sabe que el diccionario contiene la clave, diga:

let val = dict[key]!

Pero si, como suele suceder, no sabe que contiene la clave, desea obtenerla y usarla, pero solo si existe, 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
fuente
66
No te sigo. Acabo de obtener un valor (dos veces). ¿Qué más quieres?
mate
No existe tal cosa como "el primer valor"; Un diccionario no está ordenado. Si solo quiere los valores, sin las claves, diga dict.values.
mate
1
Ten cuidado porque dict.valueses opaco. Puedes recorrerlo pero eso es todo. (Está bien, no es todo, pero hágame caso.) Si quiere que se reified a una matriz, tome dict.values.array.
mate
1
@bubakazouba Pruebe esto: let d = ["hey":"ho"]; let s = d["hey"]; print(s)es opcional, como siempre ha sido.
mate
1
@Kross Esa es una pregunta muy profunda, pero por lo tanto, hágala por separado.
mate
68

¿Por qué no simplemente verificar dict.keys.contains(key)? La comprobación de dict[key] != nilno funcionará en los casos en que el valor sea nulo. Como con un diccionario [String: String?]por ejemplo.

Hans Terje Bakke
fuente
2
O en lugar de solo escribir if let val = dict[key], puede usar if let val = dict[key] as? Stringen este caso.
Okhan Okbay el
.keys.contains no ayuda a distinguir si existe una clave o si el valor es nulo. ¿Quizás en versiones rápidas anteriores? No funciona para mí en rápida 4.
Rowan Gontier
8
Esto es potencialmente muy derrochador, porque (1) llama dict.keysque crea una matriz con todas las claves, luego (2) verifica cada clave en orden hasta que encuentre una (¡o ninguna!). Soy consciente de que está tratando de abordar el caso de una [String: String?]necesaria, pero será mucho cuidado con esa solución ...
charles
3
Como mencionó @charles, esto eliminaría el beneficio de búsqueda rápida que proporciona el uso de un diccionario, por lo que desaconsejaría este, pero definitivamente es una opción válida.
businesscasual
44
Usted debe no utilizar este método, en cuenta la complejidad O (n) en vez del teórico (depende de la implementación de la función hash) O (1) de acceso directo diccionario.
Cripta
45

La respuesta aceptada let keyExists = dict[key] != nilno funcionará si el Diccionario contiene la clave pero tiene un valor nulo.

Si desea asegurarse de que el Diccionario no contiene la clave, use esto (probado en Swift 4).

if dict.keys.contains(key) {
  // contains key
} else { 
  // does not contain key
}
bergy
fuente
3
Esto es lo mismo que la respuesta de Han.
Declan McKenna
1
La comprobación == nilfunciona, incluso si el diccionario tiene valores opcionales. Esto se debe a que el resultado de la búsqueda se ajusta como un Optional. Por otro lado, para verificar si el valor buscado es realmente nilmás que ausente, puede usarlo == .some(nil).
jedwidz
1
Agregaría la eficiencia de usar este método en comparación con la búsqueda O (1) de los otros métodos. Creo que es una búsqueda de O (n)
Alberto Vega
1
@ AlbertoVega-MSFT Es O(1). Ver también: github.com/apple/swift-evolution/blob/master/proposals/… ¿Qué te hizo pensar que eraO(n) ?
Alexander - Restablece a Monica el
1
La documentación para la Dictionary.Keys.contains(...)función aquí dice que tiene O(n)complejidad, donden está la longitud de la secuencia.
Adil Hussain el
5

Parece que obtuvo lo que necesita de @matt, pero si desea una forma rápida de obtener un valor para una clave, o simplemente el primer valor si esa clave no existe:

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

Tenga en cuenta que no garantiza lo que realmente obtendrá como primer valor, simplemente sucede que en este caso devuelve "rojo".

Velocidad de velocidad del aire
fuente
1
El buen uso del ??operador me arrebató mi solución de conveniencia, pero como extensión ese valor predeterminado podría presentar inseguridades en los datos o comportamientos inesperados. Eso suena como algo que una subclase concreta Dictionarydebería emplear. ¿Con qué frecuencia necesita el primer valor en caso de un nilretorno excluyendo la funcionalidad específica de la situación?
NoodleOfDeath
1
if dictionayTemp["quantity"] != nil
    {

  //write your code
    }
Paresh Hirpara
fuente
0

Mi solución para una implementación de caché que almacena 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
fuente
0

Esto es lo que funciona para mí en Swift 3

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