7

I have following code for dictionary

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

I already remove key which have null value using below code

var keys = dic.keys.array.filter({dic[$0] is NSNull})
for key in keys {
  dic.removeValueForKey(key)
}

It works for static dictionary,But I want do it dynamically,I want to done it using function but whenever I pass dictionary as a argument it works as a let means constant so can not remove null key I make below code for that

func nullKeyRemoval(dic : [String: AnyObject]) -> [String: AnyObject]{
        var keysToRemove = dic.keys.array.filter({dic[$0] is NSNull})
        for key in keysToRemove {
            dic.removeValueForKey(key)
        }
        return dic
}

please tell me solution for this

Umesh Aawte
  • 4,590
  • 7
  • 41
  • 51
Gunja Patel
  • 289
  • 2
  • 10
  • 22
  • 1
    func nullKeyRemoval(var dic : [String: AnyObject]) -> [String: AnyObject]{ – Leo Dabus Jul 27 '15 at 07:54
  • Why you don't change your condition in filter function so you will get just the array with the data you want and you don't need to enumerate it again: ({!(dic[$0] is NSNull)}) – Greg Jul 27 '15 at 08:00

9 Answers9

12

Rather than using a global function (or a method), why not making it a method of Dictionary, using an extension?

extension Dictionary {
    func nullKeyRemoval() -> Dictionary {
        var dict = self

        let keysToRemove = Array(dict.keys).filter { dict[$0] is NSNull }
        for key in keysToRemove {
            dict.removeValue(forKey: key)
        }

        return dict
    }
}

It works with any generic types (so not limited to String, AnyObject), and you can invoke it directly from the dictionary itself:

var dic : [String: AnyObject] = ["FirstName": "Anvar", "LastName": "Azizov", "Website": NSNull(),"About": NSNull()]
let dicWithoutNulls = dic.nullKeyRemoval()
Axel Guilmin
  • 11,454
  • 9
  • 54
  • 64
Antonio
  • 71,651
  • 11
  • 148
  • 165
5

Swift 5 adds compactMapValues(_:), which would let you do

let filteredDict = dict.compactMapValues { $0 is NSNull ? nil : $0 }
Rudedog
  • 4,323
  • 1
  • 23
  • 34
3

For Swift 3.0 / 3.1 this could be helpful. Also removes NSNull objects recursive:

extension Dictionary {
    func nullKeyRemoval() -> [AnyHashable: Any] {
        var dict: [AnyHashable: Any] = self

        let keysToRemove = dict.keys.filter { dict[$0] is NSNull }
        let keysToCheck = dict.keys.filter({ dict[$0] is Dictionary })
        for key in keysToRemove {
            dict.removeValue(forKey: key)
        }
        for key in keysToCheck {
            if let valueDict = dict[key] as? [AnyHashable: Any] {
                dict.updateValue(valueDict.nullKeyRemoval(), forKey: key)
            }
        }
        return dict
    }
}
FBente
  • 2,160
  • 23
  • 34
2

Swift 3+: Remove null from dictionary

 func removeNSNull(from dict: [String: Any]) -> [String: Any] {
    var mutableDict = dict
    let keysWithEmptString = dict.filter { $0.1 is NSNull }.map { $0.0 }
    for key in keysWithEmptString {
        mutableDict[key] = ""
    }
    return mutableDict
}

Use:

let outputDict = removeNSNull(from: ["name": "Foo", "address": NSNull(), "id": "12"])

Output: ["name": "Foo", "address": "", "id": "12"]

Shiv Kumar
  • 932
  • 7
  • 12
2

Nested NSNull supported

To remove any NSNull appearance in any nested level (including arrays and dictionaries), try this:

extension Dictionary where Key == String {
    func removeNullsFromDictionary() -> Self {
        var destination = Self()
        for key in self.keys {
            guard !(self[key] is NSNull) else { destination[key] = nil; continue }
            guard !(self[key] is Self) else { destination[key] = (self[key] as! Self).removeNullsFromDictionary() as? Value; continue }
            guard self[key] is [Value] else { destination[key] = self[key]; continue }

            let orgArray = self[key] as! [Value]
            var destArray: [Value] = []
            for item in orgArray {
                guard let this = item as? Self else { destArray.append(item); continue }
                destArray.append(this.removeNullsFromDictionary() as! Value)
            }
            destination[key] = destArray as? Value
        }
        return destination
    }
}
Mojtaba Hosseini
  • 95,414
  • 31
  • 268
  • 278
1

Swift 4

A little more efficient than the other solutions. Uses only O(n) complexity.

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

    var trimmingNullValues: [String: Any] {
        var copy = self
        forEach { (key, value) in
            if value == nil {
                copy.removeValue(forKey: key)
            }
        }
        return copy as [Key: ImplicitlyUnwrappedOptional<Value>]
    }
}

Usage: ["ok": nil, "now": "k", "foo": nil].trimmingNullValues // = ["now": "k"]

If your dictionary is mutable you could do this in place and prevent the inefficient copying:

extension Dictionary where Key == String, Value == Any? {
    mutating func trimNullValues() {
        forEach { (key, value) in
            if value == nil {
                removeValue(forKey: key)
            }
        }            
    }
}

Usage: var dict: [String: Any?] = ["ok": nil, "now": "k", "foo": nil] dict.trimNullValues() // dict now: = ["now": "k"]

Charlton Provatas
  • 2,184
  • 25
  • 18
1

The cleanest way to do it, just 1 line

extension Dictionary {
    func filterNil() -> Dictionary {
        return self.filter { !($0.value is NSNull) }
    }
}
Pablo Sanchez Gomez
  • 1,438
  • 16
  • 28
0

Rather than using a global function (or a method), why not making it a method of Dictionary, using an extension?

   extension NSDictionary
    {
        func RemoveNullValueFromDic()-> NSDictionary
        {
            let mutableDictionary:NSMutableDictionary = NSMutableDictionary(dictionary: self)
            for key in mutableDictionary.allKeys
            {
                if("\(mutableDictionary.objectForKey("\(key)")!)" == "<null>")
                {
                    mutableDictionary.setValue("", forKey: key as! String)
                }
                else if(mutableDictionary.objectForKey("\(key)")!.isKindOfClass(NSNull))
                {
                    mutableDictionary.setValue("", forKey: key as! String)
                }
                else if(mutableDictionary.objectForKey("\(key)")!.isKindOfClass(NSDictionary))
                {
                    mutableDictionary.setValue(mutableDictionary.objectForKey("\(key)")!.RemoveNullValueFromDic(), forKey: key as! String)
                }
            }
            return mutableDictionary
        }
    }
Gaurav Gudaliya
  • 131
  • 1
  • 1
  • 9
  • Why would you use `NSDictionary` and `NSMutableDictionary` in `Swift` when they have native counterparts? Function names should also start with lowercase letters. Moreover, there are a lot of forced unwrapping that could crash the code and this answer only works with `String` keys. – Dávid Pásztor Aug 02 '17 at 09:43
0

Swift 4 example using reduce

let dictionary = [
  "Value": "Value",
  "Nil": nil
]

dictionary.reduce([String: String]()) { (dict, item) in

  guard let value = item.value else {
    return dict
  }

  var dict = dict
  dict[item.key] = value
  return dict
}