私はSwiftの新人であり、JSONファイルからのNULL値のフィルタリングとそれをディクショナリに設定することに問題があります。Null値でサーバーからJSON応答を取得すると、アプリがクラッシュします。
JSON応答は次のとおりです。
"FirstName": "Anvar",
"LastName": "Azizov",
"Website": null,
"About": null,
私はそれに対処するための助けをいただければ幸いです。
PD1:この時点で、私は次の方法でそれを行うことにしました。
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
}
}
}
対応する値がnilであるキーを含む配列を作成できます。
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
}
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"]
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"]
}
私はfilter
とmapValues
を組み合わせてそれを行います:
dictionary.filter { $0.value != nil }.mapValues { $0! }
上記の例を使用して、let result
と
let result = json.filter { $0.value != nil }.mapValues { $0! }
私はこれで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の場合:
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) }
}
}
Swift 4のほうがずっと簡単です。辞書のfilter
を直接使用するだけです。
jsonResult.filter { !($0.1 is NSNull) }
または、関連するキーを削除したくない場合は、これを行うことができます。
jsonResult.mapValues { $0 is NSNull ? nil : $0 }
キーを削除する代わりに、NSNull
の値をnil
に置き換えます。
このアプローチを提案して、オプションの値をフラット化し、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
は、Swift 3のvarパラメータを削除するために必要です。したがって、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
}
辞書からNSNull
の値を除外するだけだとすると、これはおそらくそれを回避するためのより良い方法の1つです。 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"]
次の関数を使用して、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;
}
compactMapValues(_:)
指定されたクロージャによる変換の結果として、nil以外の値を持つキーと値のペアのみを含む新しい辞書を返します。
let people = [
"Paul": 38,
"Sophie": 8,
"Charlotte": 5,
"William": nil
]
let knownAges = people.compactMapValues { $0 }
Swift 4はクラスDictionary
のメソッドreduce(into:_:)
を提供するため、次の関数を使用してDictionary
からnil値を削除できます。
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)")
期待されるタイプの値でそれをアンラップして、nillか空かを確認してください。そうであれば、その値を辞書から削除してください。
これは、辞書の値が空の値である場合に機能します
NSNullが任意のレベルでディクショナリにネストされるか、配列の一部になることさえある一般的なケースでは、これを解決する必要がありました。
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)
}
}
}
}
}
これが他の人に役立つことを願っていますand改善を歓迎します!
(注:これはSwift 4)です。
以下は、JSON
にsub-dictionaries
がある場合の解決策です。これにより、dictionaries
のJSON
、sub-dictionaries
がすべて通過し、JSON
からNULL(NSNull)
key-value
のペアが削除されます。
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>
}
}
NSDictionaryからNullフォームをトリミングして、クラッシュパスディクショナリをこの関数に乗せ、結果を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
}