terraform merge two maps with at least one common key - merge

I am looking to merge two maps with at least one common key. The merge function overwrite the value of common key from first map.
I have two maps with one common key .
Code
locals {
map1 = {
"key1" = "value1",
"key2" = "value2"
}
map2 = {
"key1" = "value11"
}
}
output MergedMaps {
value = merge(local.map1, local.map2)
}
Output
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
Outputs:
MergedMaps = {
"key1" = "value11"
"key2" = "value2"
}
Desired output
MergedMaps = {
“key1” = [“value1”, “value11”]
“key2” = “value2”
}
Thanks for help

Its better to have same data structure in MergedMaps, rather then mixing lists and strings. So it would be
MergedMaps = {
“key1” = [“value1”, “value11”]
“key2” = [“value2”]
}
which can be obtained using
output MergedMaps {
value = {for key in distinct(concat(keys(local.map1), keys(local.map2))):
key => flatten([lookup(local.map1, key, []),
lookup(local.map2, key, [])
])
}
}

Related

Removing Non Duplicate Keys from Two Dictionary

I have two dictionaries in Swift with few similar values which are in dynamic mode:
dict1 = ["a1":"value 1", "b1":"value2", "c1":"value 3"]
dict2 = ["b1": "value2", "d1": "value4"]
If I want to compare these two dictionaries and want to extract only the matching keys even nested, how do I about to do that?
If you want the common keys with the value in one of them :
let intersectionDict = dict1.filter { dict2.keys.contains($0.key) }
//Or
let intersectionDict2 = dict2.filter { dict1.keys.contains($0.key) }
If you want the values to match too:
let intersectionDict3 = dict1.filter { dict2[$0.key] == $0.value }
And the result is:
print(intersectionDict3) //["b1": "value2"]
As others have shown, you can do this using a filter statement. You can make it even quicker by always filtering the smaller of the two dicts, improving the time complexity from O(dict1.size) to O(min(dict1.size, dict2.size).
extension Dictionary {
func intersectingByKeys(with other: Dictionary) -> Dictionary {
let (smallerDict, largerDict) = (self.count < other.count) ? (self, other) : (other, self)
return smallerDict.filter { key, _ in largerDict.keys.contains(key) }
}
}
let dict1 = ["a1":"value 1", "b1":"value2", "c1":"value 3"]
let dict2 = ["b1": "value2", "d1": "value4"]
print(dict1.intersectingByKeys(with: dict2))
You can create a Set from the keys of one of the dictionaries and call intersection on the Set with the keys of the other dictionary.
let matchingKeys = Set(dict1.keys).intersection(dict2.keys) // {"b1"}

Swift: Convert Array of Dictionaries to Array of String based on a key

Following is my Array of Dictionaries and I want to get an Array of only strings based on particular key (contentURL key in my case).
How can I achieve it? I have came across Reduce & Filter but no one fits into my requirement.
(
{
contentURL = "https://d1shcqlf263trc.cloudfront.net/1510232473240ab.mp4";
},
{
contentURL = "https://d1shcqlf263trc.cloudfront.net/151021804847312.mp4";
},
{
contentURL = "https://d1shcqlf263trc.cloudfront.net/151021536556612.mp4";
},
{
contentURL = "https://d1shcqlf263trc.cloudfront.net/151021528690312.mp4";
}
)
Expected Output
[
"https://d1shcqlf263trc.cloudfront.net/1510232473240ab.mp4",
"https://d1shcqlf263trc.cloudfront.net/151021804847312.mp4",
"https://d1shcqlf263trc.cloudfront.net/151021536556612.mp4",
"https://d1shcqlf263trc.cloudfront.net/151021528690312.mp4"
]
Just use compactMap
let array = arrayOfDicts.compactMap {$0["contentURL"] }
var myDict: [[String : String]] = [["contentURL" : "https://d1shcqlf263trc.cloudfront.net/1510232473240ab.mp4"],["contentURL" : "https://d1shcqlf263trc.cloudfront.net/1510232473240ab.mp4"],["contentURL" : "https://d1shcqlf263trc.cloudfront.net/1510232473240ab.mp4"]]
let arr = myDict.map { $0["contentURL"] }
var stringArray:[String] = []
for (key, value) in yourArrayOfDictionary {
stringArray.append(value)
}
var arrayDict = [["contentURL":"fd"],["contentURL":"fda"],["contentURL":"fdb"],["contentURL":"fdc"]]
let arraywithOptionstring = arrayDict.map{$0["contentURL"]}
if let arr = arraywithOptionstring as? [String]{
print(arr)
}
Expected Output : ["fd", "fda", "fdb", "fdc"]
If you want to use reduce:
let arr = [
["contentURL" : "https://d1shcqlf263trc.cloudfront.net/"],
["contentURL" : "https://d1shcqlf263trc.cloudfront.net/.mp4"],
["contentURL" : "https://d1shcqlf263trc.cloudfront.net/1510232473240ab.mp4"]
]
let only = arr.reduce([String]()) { (partialRes, dictionary) -> [String] in
return partialRes + [dictionary["contentURL"]!]
}
More compact version:
let compact = arr.reduce([String]()) { $0 + [$1["contentURL"]!] }
Probably you weren't able to use reduce since you need to remember that subscripting a dictionary returns an Optional that is a different type than String
Also you can use just .map in this case.
let array = arrayOfDicts.map {$0["contentURL"]! }

How to get last entered value in a Dictionary in Swift?

How do I get the last entered value in a Dictionary in Swift? For example how would I get the value "CCC" from below:
var dictionary = Dictionary<String, String>()
dictionary.updateValue("AAA", forKey: "111")
dictionary.updateValue("BBB", forKey: "222")
dictionary.updateValue("CCC", forKey: "333")
What you are after is generally known as a Ordered Dictionary, i.e. a dictionary that remembers the order that items have been added. A quick Google search turns up a GitHub project that may suit you.
https://github.com/lukaskubanek/OrderedDictionary
With this as the type of your dictionary, you could just use:
dictionary.last // returns Optional(("CCC", "333"))
The GitHub page has details on how you would go about adding this class to your project.
If your key is sorted you can try:
var dictionary = Dictionary<String, String>()
dictionary.updateValue("AAA", forKey: "111")
dictionary.updateValue("BBB", forKey: "222")
dictionary.updateValue("CCC", forKey: "333")
print(dictionary.keys.sorted().last.map({ ($0, dictionary[$0]!) }))
The whole point of dictionaries is they're NOT ordered. This is why their lookups are generally so fast is because they partition elements without regard for their order.
If you want items in a particular order, you should use an array. Yes, "sorted dictionaries" exist, but they're really just dictionary-like interfaces on top of arrays. They contain none of the benefits that traditional dictionaries bring.
If you think you need an ordered dictionary, you probably need to consider a new structure for your data.
//If you need to build a query string for http:
let httpBody:[String : String] = [
"Street" : "1234 W 1st Street",
"Building" : "Suite 500",
"City" : "Los Angeles",
"State" : "CA",
"Zip" : "92005"
]
var idx = 0
var queryStr = ""
for (k, v) in httpBody {
if idx == httpBody.count - 1 {
queryStr += k + "=" + v
} else {
queryStr += k + "=" + v + "&"
idx += 1
}
}
print(queryStr)
//Example: for some REST API
//let bodyData:Data = queryStr.data(using: .utf8)!
//request.httpBody = bodyData
//Or this option:
var newStr = ""
for (k, v) in httpBody {
let index = httpBody.index(httpBody.startIndex, offsetBy: httpBody.count - 1)
if k == httpBody.keys[index] {
newStr += k + "=" + v
} else {
newStr += k + "=" + v + "&"
}
}
print( "\n", newStr)

Add additional text at the end of a string in an array item

Would like to add an additional text at the end of an existing text in an array-item. The code which I have till now looks like:
for var i = 0; i < arrMain.count; i++ {
if (arrMain[i] as NSString).containsString("#ID-001") {
println("OK")
// Add additional text (eg. "Test") at the end of #ID-001
}
}
Every tried combination overwrites me #ID-001...
Try:
var arrMain:[AnyObject] = ["#ID-001"]
for var i = 0; i < arrMain.count; i++ {
if (arrMain[i] as NSString).containsString("#ID-001") {
arrMain[i] = (arrMain[i] as String) + "Test"
}
}
Either do this with the for-loop you already have, e.g.
var x = [ "a", "b", "c" ]
for index in 0 ..< x.count
{
if x[index] == "b"
{
x[index] += "_hello"
}
}
or use Swift's map function:
let y = x.map({ value -> String in return value == "b" ? value + "_hello" : value })
Please note, that
The second example will make a copy of the array.
Your array needs to be a mutable array, e.g. let x = [ ... ] won't work.
Assuming I understand the question:
arrMain[i] = arrMain[i].stringByReplacingOccurrencesOfString("#ID-001", withString: "#ID-001Test")

What is the best way to iterate over a Hash in KRL?

Let's say I have a hash where I don't know the contents of the hash (so I can't use pick for this). Here is an example of the hash:
{ "key1" : "value1", "key2" : "value2", "key3" : "value3" }
I want to iterate over this hash and create the following array:
["key1=value1", "key2=value2", "key3=value3"]
My first approach would be to build a recursive function that iterates through the hash and populates the array, but I'm not sure if that can be done. In an array, I can use head() and tail() to help with the recursion, but those operators aren't available for a hash (as far as I know).
I want to initiate this from within a function because I'm doing this in a module. For example:
hash_to_array = function(h) {
// magic code here
}
manipulate_params = function(params) {
params_array = hash_to_array(params);
// more code here...
}
Mike, after impact, I will carve out time to build a keys() operator for hashes.
In the meantime, what I have done to get around this is to keep a separate array of the keys. That way, I can use map, filter and all of the set operations on the index and then use the those values as my keys for the hash operations
key_array = ["key1","key2","key3"];
my_hash = { "key1" : "value1", "key2" : "value2", "key3" : "value3" };
This really only works if you are controlling the values in the hash, but here is some example code:
global {
kvHash = { "key1" : "value1", "key2" : "value2", "key3" : "value3" };
kArray = ["key1","key2","key3"];
}
pre {
pickKey = kArray[1];
value = kvHash.pick("$.#{pickKey}");
// add a new value
newKey = "key4";
newVal = "value4";
newArray = kArray.union(newKey);
newHash = kvHash.put([newKey],newVal);
}
Noticed that I used the set operator union to keep the array full of unique values
The javascript that is generated shows what this does:
var pickKey = 'key2';
var value = 'value2';
var newKey = 'key4';
var newVal = 'value4';
var newArray = ['key1', 'key2', 'key3', 'key4'];
var newHash = {'key2' :'value2','key1' :'value1','key4' :'value4','key3' :'value3'};
Now, you can use the map or filter operators to pass each value individually to a function
c.map(function(x){x+2})
c.filter(function(x){x<5})
I'll suggest this:
foreach my_hash setting(key, value)
pre {
my_array.push("#{key}=#{value}");
}
See http://docs.kynetx.com/docs/Select
I believe I've figured this out, but the answer is a little bit of a hack.
hash_to_sorted_array = function(params, names, new_a) {
n = names.head();
val = params.pick("$.#{n}", true);
appended_array = new_a.append("#{n}=#{val.head()}");
finished_array = (names.length() == 0) => new_a |
hash_to_sorted_array(params, names.tail(), appended_array);
finished_array.sort()
}
This recursive function iterates over the names array, which contains the keys in the hash and removes the key that it processes in each iteration.
To call this function, just call:
sorted_array = hash_to_sorted_array(params, names, []);
For those unfamiliar with the head() and tail() methods of an array: head() gives you the first element in the array while tail() gives you a new array with the first element removed.
I don't really love this solution because you have to pass the names, or the keys, of the hash into the function as one of the arguments. As of this writing, I don't know of any way to extract just the keys from a hash, or I would just use that.