Swift: Nested dictionary remove all matching keys
up vote
-2
down vote
favorite
I need a dictionary extension that can remove all matching keys from an arbitrary dictionary ([String: Any]
).
An example use case could look like this:
Remove all keys from the given dictionary that match one of the following: ["test", "staging"]
[
"foo": [ "bar": "tralala" ]
"test": [ "foo": "bar", "staging": "hi"]
"aaa": [ "bbb": "cccc", "staging": "jjj"]
]
Intended result:
[
"foo": [ "bar": "tralala" ]
"aaa": [ "bbb": "cccc"]
]
swift dictionary nested mutate
add a comment |
up vote
-2
down vote
favorite
I need a dictionary extension that can remove all matching keys from an arbitrary dictionary ([String: Any]
).
An example use case could look like this:
Remove all keys from the given dictionary that match one of the following: ["test", "staging"]
[
"foo": [ "bar": "tralala" ]
"test": [ "foo": "bar", "staging": "hi"]
"aaa": [ "bbb": "cccc", "staging": "jjj"]
]
Intended result:
[
"foo": [ "bar": "tralala" ]
"aaa": [ "bbb": "cccc"]
]
swift dictionary nested mutate
What would be the intended result of the above?
– Eager Logic
Nov 11 at 21:03
I've added a better example.
– Sascha Held
Nov 11 at 21:07
Can you remove a single key/value pair from a dictionary? Can you iterate over the elements of an array?
– Martin R
Nov 11 at 21:19
add a comment |
up vote
-2
down vote
favorite
up vote
-2
down vote
favorite
I need a dictionary extension that can remove all matching keys from an arbitrary dictionary ([String: Any]
).
An example use case could look like this:
Remove all keys from the given dictionary that match one of the following: ["test", "staging"]
[
"foo": [ "bar": "tralala" ]
"test": [ "foo": "bar", "staging": "hi"]
"aaa": [ "bbb": "cccc", "staging": "jjj"]
]
Intended result:
[
"foo": [ "bar": "tralala" ]
"aaa": [ "bbb": "cccc"]
]
swift dictionary nested mutate
I need a dictionary extension that can remove all matching keys from an arbitrary dictionary ([String: Any]
).
An example use case could look like this:
Remove all keys from the given dictionary that match one of the following: ["test", "staging"]
[
"foo": [ "bar": "tralala" ]
"test": [ "foo": "bar", "staging": "hi"]
"aaa": [ "bbb": "cccc", "staging": "jjj"]
]
Intended result:
[
"foo": [ "bar": "tralala" ]
"aaa": [ "bbb": "cccc"]
]
swift dictionary nested mutate
swift dictionary nested mutate
edited Nov 11 at 21:07
asked Nov 11 at 21:00
Sascha Held
13612
13612
What would be the intended result of the above?
– Eager Logic
Nov 11 at 21:03
I've added a better example.
– Sascha Held
Nov 11 at 21:07
Can you remove a single key/value pair from a dictionary? Can you iterate over the elements of an array?
– Martin R
Nov 11 at 21:19
add a comment |
What would be the intended result of the above?
– Eager Logic
Nov 11 at 21:03
I've added a better example.
– Sascha Held
Nov 11 at 21:07
Can you remove a single key/value pair from a dictionary? Can you iterate over the elements of an array?
– Martin R
Nov 11 at 21:19
What would be the intended result of the above?
– Eager Logic
Nov 11 at 21:03
What would be the intended result of the above?
– Eager Logic
Nov 11 at 21:03
I've added a better example.
– Sascha Held
Nov 11 at 21:07
I've added a better example.
– Sascha Held
Nov 11 at 21:07
Can you remove a single key/value pair from a dictionary? Can you iterate over the elements of an array?
– Martin R
Nov 11 at 21:19
Can you remove a single key/value pair from a dictionary? Can you iterate over the elements of an array?
– Martin R
Nov 11 at 21:19
add a comment |
2 Answers
2
active
oldest
votes
up vote
0
down vote
Using Any
as a type for the values of a dictionary is discouraged. It would be preferable, in this case, to define your dictionary as [String : [String : String]]
.
If you really can't avoid using any, let's define this dictionary which has multiple levels of nested dictionaries:
let dictionary = [
"foo" : [ "bar": "tralala" ],
"test": [ "foo": "bar", "staging": "hi"],
"aaa" : [ "bbb": "cccc", "staging": "jjj"],
"d" : [ "e": "f", "g": ["h": "i", "test": "j"]],
]
And declare the keys that we'd like to remove:
let badKeys = ["test", "staging"]
Here is a recursive function that removes the unwanted keys:
func remove(_ keys: [String], from dict: [String: Any]) -> [String: Any] {
var filtered = dict.filter { !keys.contains($0.key) }
for entry in filtered {
if let value = entry.value as? [String : String] {
filtered[entry.key] = remove(badKeys, from: value)
}
}
return filtered
}
And you could use it like so:
let result = remove(badKeys, from: dictionary)
Which yields:
["aaa": ["bbb": "cccc"], "foo": ["bar": "tralala"], "d": ["e": "f", "g": ["h": "i"]]]
(Bear in mind that a dictionary is an unordered collection, so the the results may differ in order)
If you are going to use a loop rather than a map (which is a valid choice) why retain the two passes rather than do the key removal in the same loop (see my earlier answer)?
– CRD
Nov 12 at 5:58
add a comment |
up vote
-1
down vote
It is not clear what you tried and where you got stuck, you really should include this – not doing so will be why you've been down voted.
However, let's see if we can help. You state your dictionary is of type [String:Any]
and do not give any nesting limit, so we'll use the following test data:
let sampleDict : [String:Any] =
[
"foo": [ "bar": "tralala" ],
"test": [ "foo": "bar", "staging": "hi"],
"staging" : 3,
"one" : [ "two" : [ "three" : 3, "staging" : 4.2]],
"aaa": [ "bbb": "cccc", "staging": "jjj"]
]
If our algorithm copes with that it should cope with anything (famous last words...).
A straightforward algorithm using pre-defined methods and avoiding loops:
- Filter the dictionary removing any key/value pairs where the key needs to be deleted.
- Map the values in the filtered dictionary, for any value which is itself a
[String:Any]
dictionary recursively apply this algorithm to the value.
In Swift:
func removeMatchingKeys(_ dict : [String:Any], _ keysToRemove : [String]) -> [String:Any]
{
return dict
// filter keeping only those key/value pairs
// where the key isn't in keysToRemove
.filter { !keysToRemove.contains($0.key) }
// map the values in the filtered dictionary recursing
// if the value is itself a [String:Any] dictionary
.mapValues
{ if let nested = $0 as? [String:Any]
// value is dictionary, recurse
{ return removeMatchingKeys(nested, keysToRemove) }
else
// value isn't a dictionary, leave as is
{ return $0 }
}
}
Testing this with the keys:
let sampleKeys = ["test", "staging"]
The statement:
print( removeMatchingKeys(sampleDict, sampleKeys) )
Produces:
["foo": ["bar": "tralala"], "aaa": ["bbb": "cccc"], "one": ["two": ["three": 3.0]]]
The above algorithm makes two passes over the data, first to filter it and then to map it. If, and only if, this turns out to be a performance issue you can replace the two pre-defined functions filter
and map
with a simple handwritten loop which combines the operations and only passes over the data once.
Note: Above uses Xcode 10/Swift 4.2, use any other version and YMMV (i.e. syntax & pre-defined functions could easily be different) but the algorithm is still applicable.
add a comment |
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
0
down vote
Using Any
as a type for the values of a dictionary is discouraged. It would be preferable, in this case, to define your dictionary as [String : [String : String]]
.
If you really can't avoid using any, let's define this dictionary which has multiple levels of nested dictionaries:
let dictionary = [
"foo" : [ "bar": "tralala" ],
"test": [ "foo": "bar", "staging": "hi"],
"aaa" : [ "bbb": "cccc", "staging": "jjj"],
"d" : [ "e": "f", "g": ["h": "i", "test": "j"]],
]
And declare the keys that we'd like to remove:
let badKeys = ["test", "staging"]
Here is a recursive function that removes the unwanted keys:
func remove(_ keys: [String], from dict: [String: Any]) -> [String: Any] {
var filtered = dict.filter { !keys.contains($0.key) }
for entry in filtered {
if let value = entry.value as? [String : String] {
filtered[entry.key] = remove(badKeys, from: value)
}
}
return filtered
}
And you could use it like so:
let result = remove(badKeys, from: dictionary)
Which yields:
["aaa": ["bbb": "cccc"], "foo": ["bar": "tralala"], "d": ["e": "f", "g": ["h": "i"]]]
(Bear in mind that a dictionary is an unordered collection, so the the results may differ in order)
If you are going to use a loop rather than a map (which is a valid choice) why retain the two passes rather than do the key removal in the same loop (see my earlier answer)?
– CRD
Nov 12 at 5:58
add a comment |
up vote
0
down vote
Using Any
as a type for the values of a dictionary is discouraged. It would be preferable, in this case, to define your dictionary as [String : [String : String]]
.
If you really can't avoid using any, let's define this dictionary which has multiple levels of nested dictionaries:
let dictionary = [
"foo" : [ "bar": "tralala" ],
"test": [ "foo": "bar", "staging": "hi"],
"aaa" : [ "bbb": "cccc", "staging": "jjj"],
"d" : [ "e": "f", "g": ["h": "i", "test": "j"]],
]
And declare the keys that we'd like to remove:
let badKeys = ["test", "staging"]
Here is a recursive function that removes the unwanted keys:
func remove(_ keys: [String], from dict: [String: Any]) -> [String: Any] {
var filtered = dict.filter { !keys.contains($0.key) }
for entry in filtered {
if let value = entry.value as? [String : String] {
filtered[entry.key] = remove(badKeys, from: value)
}
}
return filtered
}
And you could use it like so:
let result = remove(badKeys, from: dictionary)
Which yields:
["aaa": ["bbb": "cccc"], "foo": ["bar": "tralala"], "d": ["e": "f", "g": ["h": "i"]]]
(Bear in mind that a dictionary is an unordered collection, so the the results may differ in order)
If you are going to use a loop rather than a map (which is a valid choice) why retain the two passes rather than do the key removal in the same loop (see my earlier answer)?
– CRD
Nov 12 at 5:58
add a comment |
up vote
0
down vote
up vote
0
down vote
Using Any
as a type for the values of a dictionary is discouraged. It would be preferable, in this case, to define your dictionary as [String : [String : String]]
.
If you really can't avoid using any, let's define this dictionary which has multiple levels of nested dictionaries:
let dictionary = [
"foo" : [ "bar": "tralala" ],
"test": [ "foo": "bar", "staging": "hi"],
"aaa" : [ "bbb": "cccc", "staging": "jjj"],
"d" : [ "e": "f", "g": ["h": "i", "test": "j"]],
]
And declare the keys that we'd like to remove:
let badKeys = ["test", "staging"]
Here is a recursive function that removes the unwanted keys:
func remove(_ keys: [String], from dict: [String: Any]) -> [String: Any] {
var filtered = dict.filter { !keys.contains($0.key) }
for entry in filtered {
if let value = entry.value as? [String : String] {
filtered[entry.key] = remove(badKeys, from: value)
}
}
return filtered
}
And you could use it like so:
let result = remove(badKeys, from: dictionary)
Which yields:
["aaa": ["bbb": "cccc"], "foo": ["bar": "tralala"], "d": ["e": "f", "g": ["h": "i"]]]
(Bear in mind that a dictionary is an unordered collection, so the the results may differ in order)
Using Any
as a type for the values of a dictionary is discouraged. It would be preferable, in this case, to define your dictionary as [String : [String : String]]
.
If you really can't avoid using any, let's define this dictionary which has multiple levels of nested dictionaries:
let dictionary = [
"foo" : [ "bar": "tralala" ],
"test": [ "foo": "bar", "staging": "hi"],
"aaa" : [ "bbb": "cccc", "staging": "jjj"],
"d" : [ "e": "f", "g": ["h": "i", "test": "j"]],
]
And declare the keys that we'd like to remove:
let badKeys = ["test", "staging"]
Here is a recursive function that removes the unwanted keys:
func remove(_ keys: [String], from dict: [String: Any]) -> [String: Any] {
var filtered = dict.filter { !keys.contains($0.key) }
for entry in filtered {
if let value = entry.value as? [String : String] {
filtered[entry.key] = remove(badKeys, from: value)
}
}
return filtered
}
And you could use it like so:
let result = remove(badKeys, from: dictionary)
Which yields:
["aaa": ["bbb": "cccc"], "foo": ["bar": "tralala"], "d": ["e": "f", "g": ["h": "i"]]]
(Bear in mind that a dictionary is an unordered collection, so the the results may differ in order)
edited Nov 11 at 23:48
answered Nov 11 at 23:30
Carpsen90
6,71162557
6,71162557
If you are going to use a loop rather than a map (which is a valid choice) why retain the two passes rather than do the key removal in the same loop (see my earlier answer)?
– CRD
Nov 12 at 5:58
add a comment |
If you are going to use a loop rather than a map (which is a valid choice) why retain the two passes rather than do the key removal in the same loop (see my earlier answer)?
– CRD
Nov 12 at 5:58
If you are going to use a loop rather than a map (which is a valid choice) why retain the two passes rather than do the key removal in the same loop (see my earlier answer)?
– CRD
Nov 12 at 5:58
If you are going to use a loop rather than a map (which is a valid choice) why retain the two passes rather than do the key removal in the same loop (see my earlier answer)?
– CRD
Nov 12 at 5:58
add a comment |
up vote
-1
down vote
It is not clear what you tried and where you got stuck, you really should include this – not doing so will be why you've been down voted.
However, let's see if we can help. You state your dictionary is of type [String:Any]
and do not give any nesting limit, so we'll use the following test data:
let sampleDict : [String:Any] =
[
"foo": [ "bar": "tralala" ],
"test": [ "foo": "bar", "staging": "hi"],
"staging" : 3,
"one" : [ "two" : [ "three" : 3, "staging" : 4.2]],
"aaa": [ "bbb": "cccc", "staging": "jjj"]
]
If our algorithm copes with that it should cope with anything (famous last words...).
A straightforward algorithm using pre-defined methods and avoiding loops:
- Filter the dictionary removing any key/value pairs where the key needs to be deleted.
- Map the values in the filtered dictionary, for any value which is itself a
[String:Any]
dictionary recursively apply this algorithm to the value.
In Swift:
func removeMatchingKeys(_ dict : [String:Any], _ keysToRemove : [String]) -> [String:Any]
{
return dict
// filter keeping only those key/value pairs
// where the key isn't in keysToRemove
.filter { !keysToRemove.contains($0.key) }
// map the values in the filtered dictionary recursing
// if the value is itself a [String:Any] dictionary
.mapValues
{ if let nested = $0 as? [String:Any]
// value is dictionary, recurse
{ return removeMatchingKeys(nested, keysToRemove) }
else
// value isn't a dictionary, leave as is
{ return $0 }
}
}
Testing this with the keys:
let sampleKeys = ["test", "staging"]
The statement:
print( removeMatchingKeys(sampleDict, sampleKeys) )
Produces:
["foo": ["bar": "tralala"], "aaa": ["bbb": "cccc"], "one": ["two": ["three": 3.0]]]
The above algorithm makes two passes over the data, first to filter it and then to map it. If, and only if, this turns out to be a performance issue you can replace the two pre-defined functions filter
and map
with a simple handwritten loop which combines the operations and only passes over the data once.
Note: Above uses Xcode 10/Swift 4.2, use any other version and YMMV (i.e. syntax & pre-defined functions could easily be different) but the algorithm is still applicable.
add a comment |
up vote
-1
down vote
It is not clear what you tried and where you got stuck, you really should include this – not doing so will be why you've been down voted.
However, let's see if we can help. You state your dictionary is of type [String:Any]
and do not give any nesting limit, so we'll use the following test data:
let sampleDict : [String:Any] =
[
"foo": [ "bar": "tralala" ],
"test": [ "foo": "bar", "staging": "hi"],
"staging" : 3,
"one" : [ "two" : [ "three" : 3, "staging" : 4.2]],
"aaa": [ "bbb": "cccc", "staging": "jjj"]
]
If our algorithm copes with that it should cope with anything (famous last words...).
A straightforward algorithm using pre-defined methods and avoiding loops:
- Filter the dictionary removing any key/value pairs where the key needs to be deleted.
- Map the values in the filtered dictionary, for any value which is itself a
[String:Any]
dictionary recursively apply this algorithm to the value.
In Swift:
func removeMatchingKeys(_ dict : [String:Any], _ keysToRemove : [String]) -> [String:Any]
{
return dict
// filter keeping only those key/value pairs
// where the key isn't in keysToRemove
.filter { !keysToRemove.contains($0.key) }
// map the values in the filtered dictionary recursing
// if the value is itself a [String:Any] dictionary
.mapValues
{ if let nested = $0 as? [String:Any]
// value is dictionary, recurse
{ return removeMatchingKeys(nested, keysToRemove) }
else
// value isn't a dictionary, leave as is
{ return $0 }
}
}
Testing this with the keys:
let sampleKeys = ["test", "staging"]
The statement:
print( removeMatchingKeys(sampleDict, sampleKeys) )
Produces:
["foo": ["bar": "tralala"], "aaa": ["bbb": "cccc"], "one": ["two": ["three": 3.0]]]
The above algorithm makes two passes over the data, first to filter it and then to map it. If, and only if, this turns out to be a performance issue you can replace the two pre-defined functions filter
and map
with a simple handwritten loop which combines the operations and only passes over the data once.
Note: Above uses Xcode 10/Swift 4.2, use any other version and YMMV (i.e. syntax & pre-defined functions could easily be different) but the algorithm is still applicable.
add a comment |
up vote
-1
down vote
up vote
-1
down vote
It is not clear what you tried and where you got stuck, you really should include this – not doing so will be why you've been down voted.
However, let's see if we can help. You state your dictionary is of type [String:Any]
and do not give any nesting limit, so we'll use the following test data:
let sampleDict : [String:Any] =
[
"foo": [ "bar": "tralala" ],
"test": [ "foo": "bar", "staging": "hi"],
"staging" : 3,
"one" : [ "two" : [ "three" : 3, "staging" : 4.2]],
"aaa": [ "bbb": "cccc", "staging": "jjj"]
]
If our algorithm copes with that it should cope with anything (famous last words...).
A straightforward algorithm using pre-defined methods and avoiding loops:
- Filter the dictionary removing any key/value pairs where the key needs to be deleted.
- Map the values in the filtered dictionary, for any value which is itself a
[String:Any]
dictionary recursively apply this algorithm to the value.
In Swift:
func removeMatchingKeys(_ dict : [String:Any], _ keysToRemove : [String]) -> [String:Any]
{
return dict
// filter keeping only those key/value pairs
// where the key isn't in keysToRemove
.filter { !keysToRemove.contains($0.key) }
// map the values in the filtered dictionary recursing
// if the value is itself a [String:Any] dictionary
.mapValues
{ if let nested = $0 as? [String:Any]
// value is dictionary, recurse
{ return removeMatchingKeys(nested, keysToRemove) }
else
// value isn't a dictionary, leave as is
{ return $0 }
}
}
Testing this with the keys:
let sampleKeys = ["test", "staging"]
The statement:
print( removeMatchingKeys(sampleDict, sampleKeys) )
Produces:
["foo": ["bar": "tralala"], "aaa": ["bbb": "cccc"], "one": ["two": ["three": 3.0]]]
The above algorithm makes two passes over the data, first to filter it and then to map it. If, and only if, this turns out to be a performance issue you can replace the two pre-defined functions filter
and map
with a simple handwritten loop which combines the operations and only passes over the data once.
Note: Above uses Xcode 10/Swift 4.2, use any other version and YMMV (i.e. syntax & pre-defined functions could easily be different) but the algorithm is still applicable.
It is not clear what you tried and where you got stuck, you really should include this – not doing so will be why you've been down voted.
However, let's see if we can help. You state your dictionary is of type [String:Any]
and do not give any nesting limit, so we'll use the following test data:
let sampleDict : [String:Any] =
[
"foo": [ "bar": "tralala" ],
"test": [ "foo": "bar", "staging": "hi"],
"staging" : 3,
"one" : [ "two" : [ "three" : 3, "staging" : 4.2]],
"aaa": [ "bbb": "cccc", "staging": "jjj"]
]
If our algorithm copes with that it should cope with anything (famous last words...).
A straightforward algorithm using pre-defined methods and avoiding loops:
- Filter the dictionary removing any key/value pairs where the key needs to be deleted.
- Map the values in the filtered dictionary, for any value which is itself a
[String:Any]
dictionary recursively apply this algorithm to the value.
In Swift:
func removeMatchingKeys(_ dict : [String:Any], _ keysToRemove : [String]) -> [String:Any]
{
return dict
// filter keeping only those key/value pairs
// where the key isn't in keysToRemove
.filter { !keysToRemove.contains($0.key) }
// map the values in the filtered dictionary recursing
// if the value is itself a [String:Any] dictionary
.mapValues
{ if let nested = $0 as? [String:Any]
// value is dictionary, recurse
{ return removeMatchingKeys(nested, keysToRemove) }
else
// value isn't a dictionary, leave as is
{ return $0 }
}
}
Testing this with the keys:
let sampleKeys = ["test", "staging"]
The statement:
print( removeMatchingKeys(sampleDict, sampleKeys) )
Produces:
["foo": ["bar": "tralala"], "aaa": ["bbb": "cccc"], "one": ["two": ["three": 3.0]]]
The above algorithm makes two passes over the data, first to filter it and then to map it. If, and only if, this turns out to be a performance issue you can replace the two pre-defined functions filter
and map
with a simple handwritten loop which combines the operations and only passes over the data once.
Note: Above uses Xcode 10/Swift 4.2, use any other version and YMMV (i.e. syntax & pre-defined functions could easily be different) but the algorithm is still applicable.
answered Nov 11 at 23:29
CRD
44.5k44770
44.5k44770
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53253189%2fswift-nested-dictionary-remove-all-matching-keys%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
What would be the intended result of the above?
– Eager Logic
Nov 11 at 21:03
I've added a better example.
– Sascha Held
Nov 11 at 21:07
Can you remove a single key/value pair from a dictionary? Can you iterate over the elements of an array?
– Martin R
Nov 11 at 21:19