terraform loop with a conditional key inside an object - loops

let's suppose we have this variable:
foobars = {
"first" : {
specialkeys: [
"a",
"b",
"c"
]
}
"second" : {}
}
now let's say we would like to loop over that foobars object knowing that specialkeys doesn't exist in the "second" object.
This is what I tried but it complains that
This object does not have an attribute named specialkeys
My tries:
data = flatten([
for k, v in var.foobars : [
for sk in v.specialkeys : {
special = sk,
foo = k
}
]
])

I believe you would want to do the following:
data = flatten([
for k, v in local.foobars :
[
for sk, sv in v :
[
for spec in sv : {
special = spec,
foo = k
}
]
]
])
This will output something like this:
[
{
"foo" = "first"
"special" = "a"
},
{
"foo" = "first"
"special" = "b"
},
{
"foo" = "first"
"special" = "c"
},
]

Related

Conditionals Inside Nested For Loops

I am trying to figure out a way to skip the iteration of an inner for loop if a particular attribute does not exist - in this case variables. However, I am beginning to wonder if I am expecting too much from a declarative language.
This is my input:
locals {
environments = {
dev : {
name: "development"
variables : [
{
key: "GCP_PROJECT"
value: "gcp-project-1"
},
{
key: "ANOTHER_VAR"
value: "some-value"
}
]
}
prod : {
name : "production"
variables : [
{
key: "GCP_PROJECT"
value: "gcp-project-2"
}
]
}
}
}
environments is consumed here, which basically iterates through variables to convert and store it in a data structure that is easier to work with later on:
locals {
variables = flatten([
for k, env in local.environments : [
for var, var in env.variables : { <--- Is there a way to skip an iteration if env.variables does not exist?
key: var.key
value: var.value
}
]
])
}
This loop will output something similar to this:
variables = [
{
key = "KEY"
value = "VALUE"
}
]
Which ultimately is used here:
resource "gitlab_project_variable" "variable" {
for_each = {
for i, v in local.variables : v.terraform_id => v
}
key = each.value.key
value = each.value.value
}
You can use lookup:
locals {
variables = flatten([
for k, env in local.environments : [
for var, var in lookup(env, "variables", {}) : {
key: var.key
value: var.value
}
]
])
}
I would typically recommend setting object attributes to null when you aren't using them so that the object type remains consistent in all cases, and then you can use comparison with null as the filter:
locals {
variables = flatten([
for k, env in local.environments : [
for var, var in env.variables : {
key: var.key
value: var.value
}
]
if env.variables != null
])
}
You could also set the inapplicable ones to be an empty list and then your expression would work without any extra conditions at all.
If you really do want to use inconsistent object types for your environment objects then it's not so clean to handle but you can still get something similar to the above by using the can function to detect when accessing the variables attribute would fail:
locals {
variables = flatten([
for k, env in local.environments : [
for var, var in env.variables : {
key: var.key
value: var.value
}
]
if can(env.variables)
])
}

How to combine two objects of different types in TypeScript?

Initially, I define
a = {};
b:any;
After some processing i get
a = {p:"ram", f:"sita"};
b = {"gita","mita"};
I tried to merge these two results. I tried
cart = [];
cart.push(this.a);
cart.push(this.b);
console.log(this.cart);
and get the result as below:
{p: "ram", f: "sita"}
{b: ["gita","mita"]}
I want the result like:
a ={p:"ram", f:"sita", b:"gita","mita"}
How can I achieve the above.
The example you posted isn't valid TypeScript nor JavaScript, but I assume you meant something like this:
a = { p: "1", f: "2" };
b = [ "3", "5" ];
output = { p: "1", f: "2", b: [ "3", "5" ] };
In which case, you don't need to do anything special: TypeScript fully understands what Object.assign does:
interface AWithB {
readonly a: { readonly p: string, readonly f: string };
readonly b: readonly string[];
}
const a = { p: "1", f: "2" };
const b = [ "3", "5" ];
const output = Object.assign( {}, a, { b: b } );
function requiresAWithB( arg: AWithB ): void {
console.log( arg );
}
requiresAWithB( output ); // <-- no error, because `output` matches `interface AWithB`.

Adding additional element to an existing JSON array

I have a case like, I want to add elements into a JSON array in TypeScript like below
[
{
"a" : "a1",
"b" : "b1"
}
]
Now I want to add values to the above object without creating new block, for example, I want to add key, value pair as "C": "c1" and "d": "d1". After doing this, my JSON array must look like below
[
{
"a" : "a1",
"b" : "b1"
"c" : "c1",
"d" : "d1"
}
]
What I tried:
let someData : any [] = [];
someData.push({
"a" : "a1",
"b" : b1"
})
someData.push({
"c" : "c1",
"d" : d1"
})
But it is creating two records like below, but this is wrong
[
{
"a" : "a1",
"b" : "b1"
}
{
"c" : "c1",
"d" : "d1"
}
]
as well I tried using unshift as below
someData.unshift({
"c" : "c1",
"d" : d1"
})
this is returning result object as
[
{
"a" : "a1",
"b" : "b1"
}
{
"c" : "c1",
"d" : "d1"
}
]
Is there any way to do?
For example,
for(int i =0; i<3; i++){
someData.push({
i : i+1
})
But the above block of code is creating wrong array structure, but inturn I want as below
{
0 :1,
1:2,
2:3
}
}
Its supposed to be like this...
let someData : any [] = [];
someData.push({
"a" : "a1",
"b" : b1"
})
someData[0]["c"] = "c1";
someData[0]["d"] = "d1";
So when you log the values of someData ... it will show
console.log(someData); //[{"a":"a1","b" : "b1", "c":"c1", "d":"d1"}]
for looping through values...
let valuesToPut = [["c","c1"],["d","d1"]];
for(let i = 0; i < valuesToPut.length; i++){
someData[0][valuesToPut[i][0]] = valuesToPut[i][1]
}
is little confusion between Object and Array, here you try to add some item to the Object who are store on index 0 of your array.
let try following code :
let someData : any [] = [];
// Create first index of array and create on it your Object.
someData.push({
"a" : "a1",
"b" : b1"
});
// Override first index of array by merge of previous data + new one.
someData[0] = Object.assign(someData[0], {
"c1" : "c1",
"d1" : "d1"
});
Object assign documentation
Another way to do this: Object.defineProperties()
As you mensioned, it is an array json format.
So, if you access some element in the array, you should indicate the array index.
ex:
let tempArray = [
{
"a" : "a1",
"b" : "b1"
}
]
=>
tempArray[0] has this value.
{
"a" : "a1",
"b" : "b1"
}
So, if you add some additional values to the tempArray[0], you should access the element like below :
tempArray[0]['c'] = "c1";
tempArray[0]['d'] = "d1";
//Let's start with what you're doing
let someData = [];
someData.push({
"a" : "a1",
"b" : "b1"
});
// pushes a collection of 2 key value pairs in as the first entry to someData
someData.push({
"c" : "c1",
"d" : "d1"
});
// pushes a collection of 2 key value pairs in as the second entry to someData
console.log(someData);
// what you might want to do:
someData = [];
someData.push({
"a" : "a1",
"b" : "b1"
});
// pushes a collection of 2 key value pairs in as the first entry to someData
someData[0].c = "c1";
//Sets a value to the key c in the first element of some data
someData[0].d = "d1";
//Sets a value to the key d in the first element of some data
console.log(someData)
// What you probably want to do.
someData = {};
for(let i of [1,2,3,4]){
someData[String.fromCharCode(i+96)] = String.fromCharCode(i+96)+"1";
}
console.log(someData)

How to process JSON nested array in Logstash

I have a nested field with arrays in array in JSON like the following:
{
"foo": {
"bar": [
[
"a",
"b"
],
[
"c",
"d"
]
]
}
}
The following is my config file:
input {
file {
codec => "json"
path => "pathtofile"
type => "footype"
start_position => "beginning"
}
}
filter {
json {
source => "message"
remove_field => [ "host", "message", "path" ]
}
}
output {
elasticsearch {
action => "index"
index => "bar"
hosts => [ "http://localhost:9200" ]
}
}
I got the following error:
09:40:47.725 [[main]>worker0] WARN logstash.outputs.elasticsearch -
Failed action. {:status=>400, :action=>["index", {:_id=>nil,
:_index=>"bar", :_type=>"footype", :_routing=>nil},
2017-02-13T01:40:30.387Z myconnection %{message}],
:response=>{"index"=>{"_index"=>"bar", "_type"=>"footype",
"_id"=>"AVo1IN0vK2jgwdCXqZ-q", "status"=>400,
"error"=>{"type"=>"illegal_argument_exception", "reason"=>"mapper
[foo.bar] of different type, current_type [long], merged_type
[text]"}}}}
I have a feeling that it's the array problem. I have done some research and know that array is not well supported. But I need to ingest the array in elasticsearch. Is there a way to actually do that?
Any helps will be appreciated.
I solved this by using a ruby filter:
ruby {
code => '
j = 0
for i in event.get("[foo][bar]") do
#i is an array element in the big array
l = 0
for k in i do
event.set("item#" + j.to_s + "#" + l.to_s, k)
l = l + 1
end
j = j + 1
end
'
}
This will eventually produce fields
item#0#0 = "a"
item#0#1 = "b"
item#1#0 = "c"
item#1#1 = "d"

How to add new value to dictionary [[String : String]]

I have a dictionary, which looks like this:
var dict = [["number" : "1" ], ["number" : "2" ], ["number" : "3" ]]
Now I would like to add new value "level" : "(number of level)" to each index in my dictionary, and it should looks like that:
var dict = [["number" : "1", "level" : "one"], ["number" : "2", "level" : "two" ], ["number" : "3", "level" : "three" ]]
How can I add some value inside existing dictionary in this case?
What you have listed as a dictionary is actually an array of dictionaries. You can add an element to each of the directories by simply iterating the array.
You can use an NSNumberFormatter to convert the digits you have into equivalent words:
var anArray=[["number":"1"],["number":"2"],["number":"3"]]
let numberFormatter=NSNumberFormatter()
numberFormatter.numberStyle=NSNumberFormatterStyle.SpellOutStyle
for i in 0..<anArray.count {
if let numberString=anArray[i]["number"] {
if let number=Int(numberString) {
anArray[i]["level"]=numberFormatter.stringFromNumber(number)
}
}
}
As Paulw11 pointed out, you could use NSNumberFormatter to convert the digits to words:
let dictArray = [["number" : "1" ], ["number" : "2" ], ["number" : "3" ]]
let numberFormatter = NSNumberFormatter()
numberFormatter.numberStyle = NSNumberFormatterStyle.SpellOutStyle
let mappedDictArray = dictArray.map { var d = $0; d["level"] = numberFormatter.stringFromNumber(number); return d; }
However, if you're interested in using the level key only for UI purposes, you'd be better writing a Dictionary extension, as there's no point is storing a redundant value:
extension Dictionary {
func levelString() -> String {
let numberFormatter = NSNumberFormatter()
numberFormatter.numberStyle = NSNumberFormatterStyle.SpellOutStyle
return numberFormatter.stringFromNumber(self["number"] as? Int ?? 0)
}
}
which can be used like this:
dictArray[0].level()

Resources