Swift: Как удалить нулевое значение из словаря?
Я новичок в Swift, и у меня проблема с фильтрацией значений NULL из файла 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
}
}
}
Ответы
Ответ 1
Вы можете создать массив, содержащий ключи, соответствующие значения которых равны нулю:
let keysToRemove = dict.keys.array.filter { dict[$0]! == nil }
и следующий цикл через все элементы этого массива и удалить ключи из словаря:
for key in keysToRemove {
dict.removeValueForKey(key)
}
Обновление 2017.01.17
Оператор развертывания силы является немного уродливым, хотя и безопасным, как объяснено в комментариях. Есть, вероятно, несколько других способов достижения одного и того же результата, более удобный способ использования того же метода:
let keysToRemove = dict.keys.filter {
guard let value = dict[$0] else { return false }
return value == nil
}
Ответ 2
Swift 5
Используйте compactMapValues
:
dictionary.compactMapValues { $0 }
compactMapValues
был представлен в Swift 5. Для получения дополнительной информации см. предложение Swift SE-0218.
Пример со словарем
let json = [
"FirstName": "Anvar",
"LastName": "Azizov",
"Website": nil,
"About": nil,
]
let result = json.compactMapValues { $0 }
print(result) // ["FirstName": "Anvar", "LastName": "Azizov"]
Пример, включающий разбор 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.compactMapValues { $0 }
print(result) // ["FirstName": "Anvar", "LastName": "Azizov"]
}
Swift 4
Я бы сделал это, комбинируя filter
с mapValues
:
dictionary.filter { $0.value != nil }.mapValues { $0! }
Примеры
Используйте приведенные выше примеры, просто замените let result
на
let result = json.filter { $0.value != nil }.mapValues { $0! }
Ответ 3
Я закончил с этим в 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 }
}
}
Тот же ответ, но для Swift 3:
extension Dictionary {
/// An immutable version of update. Returns a new dictionary containing self 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) }
}
}
В Swift 4 все становится намного проще. Просто используйте словарь filter
напрямую.
jsonResult.filter { !($0.1 is NSNull) }
Или, если вы не хотите удалять соответствующие ключи, вы можете сделать это:
jsonResult.mapValues { $0 is NSNull ? nil : $0 }
Который заменит значения NSNull
на nil
вместо удаления ключей.
Ответ 4
Предполагая, что вы просто хотите отфильтровать любые значения 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 ($0.1 is NSNull) ? .None : $0
})
// clean -> ["LastName": "Azizov", "FirstName": "Anvar"]
Ответ 5
Предлагая этот подход, выравнивает необязательные значения, а также совместим с Swift 3
String
ключ, необязательный словарь значений AnyObject?
с значениями nil:
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"]
Повторная декларация var dict = dict
необходима из-за удаления параметров var в swift 3, поэтому для быстрого 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
}
Ответ 6
Поскольку Swift 4 предоставляет метод reduce(into:_:)
для класса Dictionary
вы можете удалить nil-values из 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)")
Ответ 7
Swift 5
compactMapValues (_ :)
Возвращает новый словарь, содержащий только пары ключ-значение, которые имеют ненулевые значения, в результате преобразования заданным замыканием.
let people = [
"Paul": 38,
"Sophie": 8,
"Charlotte": 5,
"William": nil
]
let knownAges = people.compactMapValues { $0 }
Ответ 8
Мне просто пришлось решить это для общего случая, когда 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 {!($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)
}
}
}
}
}
Я надеюсь, что это будет полезно для других, и я приветствую улучшения!
(Примечание: это Swift 4)
Ответ 9
Ниже приведено решение, когда JSON
имеет sub-dictionaries
.
Это проведет все dictionaries
, sub-dictionaries
из 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({ $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>
}
}
Ответ 10
Вы можете заменить пустые значения пустой строкой, используя следующую функцию.
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;
}
Ответ 11
Обрезать нулевую форму NSDictionary, чтобы получить толчок при сбое Pass Dictionary к этой функции и получить результат как 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
}
Ответ 12
Попытайтесь развернуть его с ожидаемым типом значений и проверьте, нет ли пустых значений, если да, то удалите это значение из словаря.
Это будет работать, если значения в словаре имеют пустые значения