Swift: как удалить значение null из словаря?
Я новичок в Swift, и у меня проблема с фильтрацией нулевых значений из файла JSON и установкой его в словарь. Я получаю ответ JSON с сервера с нулевыми значениями, и он аварийно завершает работу моего приложения.
вот ответ JSON:
"FirstName": "Anvar",
"LastName": "Azizov",
"Website": null,
"About": null,
Я буду очень признателен за помощь, чтобы справиться с ней.
UPD1: в этот момент я решил сделать это следующим образом:
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
}
}
}
9 ответов
вы можете создать массив, содержащий ключи, соответствующие значения которых равны нулю:
let keysToRemove = dict.keys.array.filter { dict[]! == nil }
и следующий цикл через все элементы этого массива и удалить ключи из словаря:
for key in keysToRemove {
dict.removeValueForKey(key)
}
обновление 2017.01.17
оператор разворачивания силы немного уродлив, хотя и безопасен, как объясняется в комментариях. Вероятно, есть несколько других способов достичь того же результата, более красивый способ того же метода есть:
let keysToRemove = dict.keys.filter {
guard let value = dict[] else { return false }
return value == nil
}
Я закончил с этим в Swift 2:
extension Dictionary where Value: AnyObject {
var nullsRemoved: [Key: Value] {
let tup = filter { !(.1 is NSNull) }
return tup.reduce([Key: Value]()) { (var r, e) in r[e.0] = e.1; return r }
}
}
тот же ответ, но для 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 { !(.1 is NSNull) }
return tup.reduce([Key: Value]()) { .0.updatedValue(.1.value, forKey: .1.key) }
}
}
все становится намного проще в Swift 4. Просто используйте словарь filter
напрямую.
jsonResult.filter { !(.1 is NSNull) }
или если вы не хотите удалять соответствующие ключи, вы можете сделать это:
jsonResult.mapValues { is NSNull ? nil : }
который заменит NSNull
значения nil
вместо удаления ключей.
предполагая, что вы просто хотите отфильтровать любой NSNull
значения из словаря, это, вероятно, один из лучших способов сделать это. Это будущее-защищено от Swift 3, Насколько я знаю на данный момент:
(С спасибо AirspeedVelocity для расширения, переведенного на 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 (.1 is NSNull) ? .None :
})
// clean -> ["LastName": "Azizov", "FirstName": "Anvar"]
предлагая этот подход, сглаживает необязательные значения, а также совместим с Swift 3
String
ключ, дополнительно AnyObject?
словарь значений с нулевыми значениями:
let nullableValueDict: [String : AnyObject?] = [
"first": 1,
"second": "2",
"third": nil
]
// ["first": {Some 1}, "second": {Some "2"}, "third": nil]
значения nil удалены и преобразованы в необязательный словарь значений
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"]
Redeclaration var dict = dict
необходимо из-за удаления параметров var в swift 3, поэтому для 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, было бы;
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
}
поскольку Swift 4 предоставляет метод reduce(into:_:)
класс Dictionary
вы можете удалить нулевые значения из Dictionary
со следующей функцией:
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)
}
}
}
вы можете проверить это так:
let testingDict = removeNilValues(dict: ["1":nil, "2":"b", "3":nil, "4":nil, "5":"e"])
print("test result is \(testingDict)")
Swift 4
Я бы сделал это, объединив filter
С mapValues
:
dictionary.filter { .value != nil }.mapValues { ! }
полный пример:
let json = [
"FirstName": "Anvar",
"LastName": "Azizov",
"Website": nil,
"About": nil,
]
let result = json.filter { .value != nil }.mapValues { ! }
print(result) // ["FirstName": "Anvar", "LastName": "Azizov"]
Мне просто нужно было решить это для общего случая, когда NSNulls могут быть вложены в словарь на любом уровне или даже быть частью массива:
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 {!( 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)
}
}
}
}
}
Я надеюсь, что это поможет другим и приветствую улучшений!
(Примечание: это Swift 4)
следующее решение, когда JSON
есть sub-dictionaries
.
Это пройдет-через все dictionaries
, sub-dictionaries
of JSON
и удалить NULL(NSNull)
key-value
пара от 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({ .value is NSNull }).map({ })
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({ .value is NSNull }).map({ })
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>
}
}
вы можете заменить значения null на пустую строку следующей функцией.
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;
}