web-dev-qa-db-fra.com

Swift: Comment supprimer une valeur nulle du dictionnaire?

Je suis nouveau dans Swift et j'ai un problème avec le filtrage des valeurs NULL à partir d'un fichier JSON et sa définition dans Dictionary. Je reçois une réponse JSON du serveur avec des valeurs null et mon application se bloque. 

Voici la réponse JSON:

"FirstName": "Anvar",
"LastName": "Azizov",
"Website": null,
"About": null,

Je serai très apprécié de l'aide pour y faire face.

UPD1: À ce moment, j'ai décidé de le faire d'une autre manière: 

if let jsonResult = responseObject as? [String: AnyObject] {                    
    var jsonCleanDictionary = [String: AnyObject]()

    for (key, value) in enumerate(jsonResult) {
      if !(value.1 is NSNull) {
         jsonCleanDictionary[value.0] = value.1
      }
    }
}
13
Anvar Azizov

Vous pouvez créer un tableau contenant les clés dont les valeurs correspondantes sont nil:

let keysToRemove = dict.keys.array.filter { dict[$0]! == nil }

et ensuite, parcourez tous les éléments de ce tableau et supprimez les clés du dictionnaire:

for key in keysToRemove {
    dict.removeValueForKey(key)
}

Mise à jour 2017.01.17

L'opérateur de déploiement de la force est un peu moche, bien que sûr, comme expliqué dans les commentaires. Il existe probablement plusieurs autres moyens d'obtenir le même résultat. Une méthode plus esthétique de la même méthode est la suivante:

let keysToRemove = dict.keys.filter {
  guard let value = dict[$0] else { return false }
  return value == nil
}
10
Antonio

J'ai fini avec ceci dans Swift 2 :

extension Dictionary where Value: AnyObject {

    var nullsRemoved: [Key: Value] {
        let tup = filter { !($0.1 is NSNull) }
        return tup.reduce([Key: Value]()) { (var r, e) in r[e.0] = e.1; return r }
    }
}

Même réponse mais pour Swift 3 :

extension Dictionary {

    /// An immutable version of update. Returns a new dictionary containing self's values and the key/value passed in.
    func updatedValue(_ value: Value, forKey key: Key) -> Dictionary<Key, Value> {
        var result = self
        result[key] = value
        return result
    }

    var nullsRemoved: [Key: Value] {
        let tup = filter { !($0.1 is NSNull) }
        return tup.reduce([Key: Value]()) { $0.0.updatedValue($0.1.value, forKey: $0.1.key) }
    }
}

Les choses deviennent beaucoup plus faciles dans Swift 4 . Il suffit d'utiliser la variable filter du dictionnaire directement.

jsonResult.filter { !($0.1 is NSNull) }

Ou si vous ne souhaitez pas supprimer les clés pertinentes, vous pouvez procéder comme suit:

jsonResult.mapValues { $0 is NSNull ? nil : $0 }

Ce qui remplacera les valeurs NSNull par nil au lieu de supprimer les clés.

6
Daniel T.

Swift 4

Je le ferais en combinant filter avec mapValues:

dictionary.filter { $0.value != nil }.mapValues { $0! }

Exemple avec dictionnaire

let json = [
    "FirstName": "Anvar",
    "LastName": "Azizov",
    "Website": nil,
    "About": nil,
]

let result = json.filter { $0.value != nil }.mapValues { $0! }
print(result) // ["FirstName": "Anvar", "LastName": "Azizov"]

Exemple incluant l'analyse JSON

let jsonText = """
  {
    "FirstName": "Anvar",
    "LastName": "Azizov",
    "Website": null,
    "About": null
  }
  """

let data = jsonText.data(using: .utf8)!
let json = try? JSONSerialization.jsonObject(with: data, options: [])
if let json = json as? [String: Any?] {
    let result = json.filter { $0.value != nil }.mapValues { $0! }
    print(result) // ["FirstName": "Anvar", "LastName": "Azizov"]
}
6
Marián Černý

Suggérant cette approche, aplatit les valeurs optionnelles et est également compatible avec Swift 3.

Clé String, dictionnaire de valeurs AnyObject? facultatif avec des valeurs nulles:

let nullableValueDict: [String : AnyObject?] = [
    "first": 1,
    "second": "2",
    "third": nil
]

// ["first": {Some 1}, "second": {Some "2"}, "third": nil]

valeurs nulles supprimées et transformées en dictionnaire de valeurs non optionnelles

nullableValueDict.reduce([String : AnyObject]()) { (dict, e) in
    guard let value = e.1 else { return dict }
    var dict = dict
    dict[e.0] = value
    return dict
}

// ["first": 1, "second": "2"]

La redéclaration var dict = dict est nécessaire en raison de la suppression des paramètres var dans Swift 3, ce qui est le cas pour Swift 2.1;

nullableValueDict.reduce([String : AnyObject]()) { (var dict, e) in
    guard let value = e.1 else { return dict }
    dict[e.0] = value
    return dict
}

Swift 4, serait;

let nullableValueDict: [String : Any?] = [
    "first": 1,
    "second": "2",
    "third": nil
]

let dictWithoutNilValues = nullableValueDict.reduce([String : Any]()) { (dict, e) in
    guard let value = e.1 else { return dict }
    var dict = dict
    dict[e.0] = value
    return dict
}
3
Eralp Karaduman

En supposant que vous souhaitiez simplement filtrer les valeurs NSNull d'un dictionnaire, c'est probablement l'une des meilleures façons de procéder. Il est à l’avenir contre Swift 3, pour autant que je sache pour le moment:

(Merci à AirspeedVelocity pour l'extension, traduit en Swift 2)

import Foundation

extension Dictionary {
/// Constructs [key:value] from [(key, value)]

  init<S: SequenceType
    where S.Generator.Element == Element>
    (_ seq: S) {
      self.init()
      self.merge(seq)
  }

  mutating func merge<S: SequenceType
    where S.Generator.Element == Element>
    (seq: S) {
      var gen = seq.generate()
      while let (k, v) = gen.next() {
        self[k] = v
      }
  }
}

let jsonResult:[String: AnyObject] = [
  "FirstName": "Anvar",
  "LastName" : "Azizov",
  "Website"  : NSNull(),
  "About"    : NSNull()]

// using the extension to convert the array returned from flatmap into a dictionary
let clean:[String: AnyObject] = Dictionary(
  jsonResult.flatMap(){ 
    // convert NSNull to unset optional
    // flatmap filters unset optionals
    return ($0.1 is NSNull) ? .None : $0
  })
// clean -> ["LastName": "Azizov", "FirstName": "Anvar"]
3
ColGraff

Puisque Swift 4 fournit la méthode reduce(into:_:) pour la classe Dictionary, vous pouvez supprimer les valeurs nil de Dictionary avec la fonction suivante:

func removeNilValues<K,V>(dict:Dictionary<K,V?>) -> Dictionary<K,V> {

    return dict.reduce(into: Dictionary<K,V>()) { (currentResult, currentKV) in

        if let val = currentKV.value {

            currentResult.updateValue(val, forKey: currentKV.key)
        }
    }
}

Vous pouvez le tester comme ça:

let testingDict = removeNilValues(dict: ["1":nil, "2":"b", "3":nil, "4":nil, "5":"e"])
print("test result is \(testingDict)")
1
Roman Podymov

Je devais simplement résoudre ce problème dans un cas général où NSNulls pouvait être imbriqué dans le dictionnaire à n'importe quel niveau, ou même faire partie d'un tableau:

extension Dictionary where Key == String, Value == Any {

func strippingNulls() -> Dictionary<String, Any> {

    var temp = self
    temp.stripNulls()
    return temp
}

mutating func stripNulls() {

    for (key, value) in self {
        if value is NSNull {
            removeValue(forKey: key)
        }
        if let values = value as? [Any] {
            var filtered = values.filter {!($0 is NSNull) }

            for (index, element) in filtered.enumerated() {
                if var nestedDict = element as? [String: Any] {
                    nestedDict.stripNulls()

                    if nestedDict.values.count > 0 {
                        filtered[index] = nestedDict as Any
                    } else {
                        filtered.remove(at: index)
                    }
                }
            }

            if filtered.count > 0 {
                self[key] = filtered
            } else {
                removeValue(forKey: key)
            }
        }

        if var nestedDict = value as? [String: Any] {

            nestedDict.stripNulls()

            if nestedDict.values.count > 0 {
                self[key] = nestedDict as Any
            } else {
                self.removeValue(forKey: key)
            }
        }
    }
}

}

J'espère que cela aidera les autres et j'apprécie les améliorations!

(Note: c'est Swift 4)

0
buildsucceeded

Voici la solution: lorsque JSON a sub-dictionaries. Cela va parcourir toutes les dictionaries, sub-dictionaries de JSON et supprimer NULL(NSNull)key-value paire de la JSON.

extension Dictionary {

    func removeNull() -> Dictionary {
        let mainDict = NSMutableDictionary.init(dictionary: self)
        for _dict in mainDict {
            if _dict.value is NSNull {
                mainDict.removeObject(forKey: _dict.key)
            }
            if _dict.value is NSDictionary {
                let test1 = (_dict.value as! NSDictionary).filter({ $0.value is NSNull }).map({ $0 })
                let mutableDict = NSMutableDictionary.init(dictionary: _dict.value as! NSDictionary)
                for test in test1 {
                    mutableDict.removeObject(forKey: test.key)
                }
                mainDict.removeObject(forKey: _dict.key)
                mainDict.setValue(mutableDict, forKey: _dict.key as? String ?? "")
            }
            if _dict.value is NSArray {
                let mutableArray = NSMutableArray.init(object: _dict.value)
                for (index,element) in mutableArray.enumerated() where element is NSDictionary {
                    let test1 = (element as! NSDictionary).filter({ $0.value is NSNull }).map({ $0 })
                    let mutableDict = NSMutableDictionary.init(dictionary: element as! NSDictionary)
                    for test in test1 {
                        mutableDict.removeObject(forKey: test.key)
                    }
                    mutableArray.replaceObject(at: index, with: mutableDict)
                }
                mainDict.removeObject(forKey: _dict.key)
                mainDict.setValue(mutableArray, forKey: _dict.key as? String ?? "")
            }
        }
        return mainDict as! Dictionary<Key, Value>
    }
 }
0
Rashesh Bosamiya

Supprimez la forme Null de NSDictionary pour éviter les accidents Transférer un dictionnaire pour cette fonction et obtenir le résultat sous NSMutableDictionary

func trimNullFromDictionaryResponse(dic:NSDictionary) -> NSMutableDictionary {
    let allKeys = dic.allKeys
    let dicTrimNull = NSMutableDictionary()
    for i in 0...allKeys.count - 1 {
        let keyValue = dic[allKeys[i]]
        if keyValue is NSNull {
            dicTrimNull.setValue("", forKey: allKeys[i] as! String)
        }
        else {
            dicTrimNull.setValue(keyValue, forKey: allKeys[i] as! String)
        }
    }
    return dicTrimNull
}
0
Sandeep Kalia

Vous pouvez remplacer les valeurs NULL par une chaîne vide en suivant la fonction.

func removeNullFromDict (dict : NSMutableDictionary) -> NSMutableDictionary
{
    let dic = dict;

    for (key, value) in dict {

        let val : NSObject = value as! NSObject;
        if(val.isEqual(NSNull()))
        {
            dic.setValue("", forKey: (key as? String)!)
        }
        else
        {
            dic.setValue(value, forKey: key as! String)
        }

    }

    return dic;
}
0
Anita