Is it possible to do this in terraform?
for i in range(10):
if var != "" and i > 2:
# Something to do
elif var != "" and i < 2:
# Something to do
else:
# Something else to do
What i want to achieve is to create list but i need if/else statement in for loop. What i have achieved so far is:
for i in range(10):
if var != "":
# Something to do
It's hard to answer this question because it seems like you already decided on a solution to a problem but you haven't actually stated what the underlying problem is. However, I will try to answer directly what you asked, nonetheless.
When thinking about approaching problems in Terraform it's best to think from the perspective of constructing values from expressions using other values, rather than writing imperative statements to describe how to construct those. In this case, I would try to pick your problem into two parts:
Doing something different when var is set to the empty string vs. other cases.
Doing something different when constructing the first two elements of a list, but only in the non-empty string case.
The first of those sounds like a conditional expression, because you want to choose between two possible outcomes based on a condition:
locals {
example = (
var != "" ?
(expression for normal case) :
(expression for empty case)
)
}
You haven't included details about what ought to happen in the "empty case", but since you suggested you would use a loop in an imperative language I assume that your problem would best map to a for expression in Terraform.
locals {
example = (
var != "" ?
(expression for normal case) :
[ for i in range(10) : (something based on i) ]
)
}
The "normal case" has the extra detail of doing something different based on whether the index is less than two:
locals {
example = (
var != "" ?
[
for i in range(10) : (
i < 2 ?
(something based on i) :
(something else based on i)
)
]
[ for i in range(10) : (something based on i) ]
)
}
(Your original question used i < 2 and i > 2, and my answer above admittedly uses i < 2 and i >= 2 instead; I made an assumption that this interpretation was more likely, but this answer won't work if you really do need to do something special when i == 2 that's different to when it's less than or greater than.)
I can't take this answer any further without more detail about the underlying problem you're trying to solve, but since you seem to be coming from familiarity with Python I do have two general mappings for you that might help you translate from Python-ish approaches to Terraform-ish approaches:
Terraform's conditional operator c ? t : f is equivalent to Python's conditional expressions.
Terraform's for expressions are equivalent to Python's list comprehensions.
In both cases these constructs have similar capabilities but different syntax. If you feel more comfortable exploring in Python first then I'd suggest using these particular Python constructs to build your solution and then you should be able to translate the result to an equivalent Terraform expression.
Terraform is (sadly) not a programming language, so it's not easy (or possible) to convert any pseudo-code to HCL.
Anyway, for your specific case you need to combine two things:
the equivalent of i of your pseudo-code in Terraform would be: count.index (in case you use count)
the if-else-else is probably something like:
(local.a != "" && count.index > 2) ? "option 1" : ((local.a != "" && count.index < 2) ? "option 2" : "option 3" )
(not tested)
Also it might come across as nit-picking, but a tiny remark is "Something to do" in Terraform you rather not declare things to be done (as it's not a procedural programming language), but rather desired state of things.
Related
Looking for more R-ish ways of implementing a "for" loop with "subset", that will lend itself to implementation in R Markdown
I have a large dataset, that can be summarised as:
StudentID, Unit, TutorialID, SemesterID, Mark, Grade
I have written the following code, which seems to work OK. This reflects my background as an imperative programmer of long ago (and the fact that I am self-taught in R). Partly, I am curious as to how to write the "sequential application of a group of functions" to "successive subsets" in a way that is more R-ish.
ListOfUnits <- unique (Dataset$Unit)
for (val in ListOfUnits) {
EachUnit <- subset(Dataset, Unit == val)
boxplot(Mark ~ TutorialID, ylim=c(0,100), data=EachUnit,outline=TRUE,main=val)
aggregate(x= EachUnit$Mark, by = list(EachUnit$Campus), FUN=mean, na.rm=TRUE)
aggregate(x= EachUnit$Mark, by = list(EachUnit$Campus), FUN=sd, na.rm=TRUE)
if (nrow(count(EachUnit$TutorialID)) >= 2) {
# Here I have code to run an ANOVA and the Tukey HSD test for any difference
# between means among tutorial groups, culminating in
bar.group(pp$groups,ylim=c(0,100),density=400,border="black",main=val)
}
else {
}
}
I have also tried my hand at creating an R Markdown script to genearte a report on the multitude of units that exist. What seemed a promising approach involves knitr::knit_expand(file = "file_location" ... early efforts seemed to be good, but when I included "for" or "if" statements in either the 'parent' or 'child' file, it either generated errors or did not run as expected.
My conclusion is that the basic routine is insufficiently "R-ish", hence the question above.
But an immediate follow-on question is "how to achieve the above in R Markdown, so as to produce a report"?
Thank you
The following prototype does the job:
Unit_Statistics <- function(Unit) {
boxplot(Mark ~ Campus, ylim=c(0,100),data=Unit,outline=TRUE,main=Unit$Unit[1])
}
bitbucket <- Dataset %>% group_by(Unit) %>% do(Stats=Unit_Statistics(.))
I've started using Google Cloud Deployment recently, having come from AWS CloudFormation (and Terraform alongside) and I'm really struggling to use Jinja in dynamic ways that I do so simplistically in the others.
My current issue is that my deployment is entirely dynamic based on some user input, and so in AWS CF and Terraform (which points at both AWS and GCP) I use maps to get settings determined by a previous choice. The following is an example from Terraform:
variable "Cluster_Instance_Map" {
type = map
default = {
"C1" = "Single-Node : 0 : A : B"
"C2" = "Multi-Node : 2 : Q : R"
"C3" = "Multi-Node : 4 : X : Y"
"C4" = "Multi-Node : 8 : S : T"
...
}
}
And then I would, for example, grab the first value for the respective row by using the Cluster_Config_Choice variable chosen from 'C1, C2, C3, C4' by a user previously as follows:
split ( " : ", var.Cluster_Instance_Map[ var.Cluster_Config_Choice ] ) [0]
Thus far, I've really struggled to re-create this type of variable in Jinja for GCP. I'm new to GCP in general, but also Jinja, and what I find online is, for the most part, confusing me more than anything, and so any help with this is muchly appreciated!
--- Edit ---
As per request, I will give some detail into what I've done with Jinja thus far, although it sadly isn't too much. My initial idea is via the use of LIST and SPLIT. I figure I could do something like the following:
{% set list_example = ({ "A" : "1 ; 2 ; 3", "B" : "4 ; 5 ; 6" }) %}
{{ list_example [ user_input_variable ].split(';')[1] }}
And the second line would then return, "5" if the user selected B, for example. I did make that code up, though (second line) so it doesn't work for syntax errors (100% expected) but I don't know if it's even close.
Is a LIST and SPLIT the way to go? Or are there MAP-like functions available that I am missing out on..
I also don't know how to put my SET function across multiple lines without erroring, so sorry for the mess above. Though I assume Google can tell me that when I'm not busy! >.>
Hope this helps clarify things.
After going around in circles, and learning very minimal in the process, I've actually realised that my answer was what I had already tried... The code I posted above saying doesn't work, works - Though it turns out there were a multitude of other reasons it didn't, but not related directly to itself.
And so, my answer to re-create what I have as a 'map' in Terraform/AWS CF is as follows:
{% set Cluster_Instance_Map = ({
"C1" : "Single-Node : 0 : A : B",
"C2" : "Multi-Node : 2 : Q : R",
"C3" : "Multi-Node : 4 : X : Y",
"C4" : "Multi-Node : 8 : S : T",
...
}) %}
{% set user_input_variable = "C3" %}
{{ Cluster_Instance_Map [ user_input_variable ].split(':')[1] }}
And the final piece would return the number 4, in that case. My code is also across multiple lines, so it's much easier to read now as well - This also was an issue unrelated (as mentioned, I'm entirely new to Google and Jinja, so lessons learned).
I must say, though, the documentation for Google Cloud really is terrible in comparison to others. Very easy to loose yourself. So even though the above doesn't really need an answer, figure best to put this here just in case others have a similar question.
Option 1:
className={
data.msg.length > 48
? `${classes.message} ${classes.longMessage}`
: `${classes.message}`
}
Option 2:
className={`${classes.message} ${
data.msg.length > 48 ? classes.longMessage : ""
}`}
Is there any performance difference too? Thanks.
They're pretty much the same, and choosing one over the other will have no noticeable effect on the performance of your app. The only thing you will want to consider in this circumstance is the readability - in which case I would argue that the first example would be preferable, but that's entirely subjective.
I totally agree with the readability what #noob mentioned in the answer earlier.
From data side let me configure the following below:
// I assume you are getting this from an API or somewhere else
const data = {
msg: 'Testing text for stackoverflow'
};
From that perspective my suggestion would be to go with the following option:
let messageClassName = `${classes.message}`;
if (data.msg.length > 48) {
messageClassName = `${classes.message} ${classes.longMessage}`;
}
return (
<div className={messageClassName}>
{data.msg}
</div>
)
Most of the time readability helps.
My guess is the top one is faster, here is my reasoning:
Both will have one ternary expression - Where you are using it does not change it's speed, but the commands executed after expression will, and this is what make the first faster, eg:
Case expression TRUE: ( no - difference )
Both will have to concatenate
Case expression FALSE: ( Top one faster )
Top one just evaluate
Botton one concatenate and evaluate
But to do this is too much preciosim.
I'm writing automated tests for one site. There's a page with all items added to the cart. Maximum items is 58. Instead of verification of each element one by one I decided to create 2 arrays filled with strings: 1 with correct names : String and 1 with names : String I got from the site. Then I compare those 2 arrays with contentEquals.
If that comparison fails, how do I know which element exactly caused comparison fail?
Short simple of what I have now:
#Test
fun verifyNamesOfAddedItems () {
val getAllElementsNames = arrayOf(materials.text, element2.text,
element3.text...)
val correctElementsNames = arrayOf("name1", "name2", "name3"...)
val areArraysEqual = getAllElementsNames contentEquals correctElementsNames
if (!areArraysEqual) {
assert(false)
} else {
assert(true)
}
}
This test fails if 2 arrays are not the same but it doesn't show me the details, so is there a way to see more details of fail, e.g. element that failed comparison?
Thanks.
I recommend using a matcher library like Hamcrest or AssertJ in tests. They provide much better error messages for cases like this. In this case with Hamcrest it would be:
import org.hamcrest.Matchers.*
assertThat(getAllElementsNames, contains(*correctElementsNames))
// or just
assertThat(getAllElementsNames, contains("name1", "name2", "name3", ...))
There are also matcher libraries made specifically for Kotlin: https://github.com/kotlintest/kotlintest, https://yobriefca.se/expect.kt/, https://github.com/winterbe/expekt, https://github.com/MarkusAmshove/Kluent, probably more. Tests using them should be even more readable, but I haven't tried any of them. Look at their documentation and examples and pick the one you like.
You need to find the intersection between the two collections. Intersection will be the common elements. After than removing the intersection collection from the collection you want to perform the test will give you the complementary elements.
val intersection = getAllElementsNames.intersect(correctElementsNames)
getAllElementsNames.removeAll(intersection)
Using ng-pluralize with this template:
Your subscription <span ng-pluralize count="::vm.account.subscription.expirationDays"
when="{ '-1': 'has expired!',
'0': 'expires today!',
'one': 'expires tomorrow.',
'other': 'expires in {} days.'}"></span>
Yields the following result:
Expiration Days Label
-1 Your subscription has expired!
0 Your subscription expires today!
1 Your subscription expires tomorrow!
X Your subscription expires in X days.
However, this breaks as soon as a subscription expires 2 days ago.
Is it possible to define a boolean expression as a when clause so that
vm.account.subscription.expirationDays < 0 === 'has expired!'
Currently I'm having to handle expired labels in a different element which kind of defeats the purpose of using ng-pluralize.
It looks like your scenario is, albeit perhaps a common one, too complex for ngPluralize. I also doubt it will change, because ngPluralize is based on "plural categories":
http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html
The problem being that en-US, Angular's default locale, defines only the categories "one" and "other". Anything that doesn't fall into those categories is explicitly defined (or inferred by $locale.pluralCat).
The three best options for your scenario that immediately come to me are:
1) Simplest would be to have two objects:
when="count >=0 ? positivePlurals : negativePlurals"
where, of course $scope.count = vm.account.subscription.expirationDays, positivePlurals is your positive phrases and negativePlurals is your negative phrases.
2) Wrap a localization library that supports many-or-custom plural rules (such as i18next) in a directive, and use that instead. I'm not very familiar with the popular angular-translate, but at first glance it doesn't seem to support custom pluralization rules. It does, however, allow logic in interpolation, so you might get away with that.
3) Write a directive similar to ngPluralize that supports ("-other", "x", "other"). The source for ngPluralize is available here. It would probably be as simple as modifying the statement at L211 in a way similar to:
var countIsNaN = isNaN(count);
var countIsNegative = count < 0;
if (!countIsNaN && !(count in whens)) {
// If an explicit number rule such as 1, 2, 3... is defined, just use it.
// Otherwise, check it against pluralization rules in $locale service.
count = $locale.pluralCat(count - offset);
if(countIsNegative){
count = '-'+count; // "-one", "-other"
}
}