Is there a cleaner way to add “else if” to assignment conditional in Awk, etc.?
-
02-10-2019 - |
Question
Certain languages like awk script allow for conditional assignments. For example, say you had a list file in the format:
<item name, no spaces> <price as float>
e.g.
Grape 4.99
JuicyFruitGum 0.45
Candles 5.99
And you wanted to tax everything over $1... you could use the awk script:
awk '{a=($2>1.00)?$2*1.06:$2; print a}' prices.data
...which uses conditional assignment to shorten the syntax.
But say you wanted to also offer $1 off all items over $20 and $2 off items over $40. Well in a language like c you would typically do something like:
if (price > 40.00) {
price-=2;
price *= 1.06;
}
else if ( price > 20.00 && price <= 40.00 ) {
price--;
price *= 1.06;
}
else if ( price > 1.00 ) {
price*=1.06;
}
... well I discovered you could kludge awk or other scripting languages into COMPOUND assignment. e.g.:
awk '{a=($2>1.00)?($2>20.00)?($2-1)*1.06:($2>40.00)?($2-2)*1.06:$2*1.06:$2; print a}' prices.data
My questions are that
a) is compound assignment (like this) generally universally compatible with scripting languages that support conditional assignment?
b) Is there a non-kludge way to do multi-conditional assignment in awk script?
To clarify: I am talking exclusively about the shorthand for assignment (<...>?<...>:<...>;, not traditional conditional assignment, which I already know how to do c-like compound assignment for in Awk script. As a side note, as to why I might use shorthand, I think the merit is obvious -- that it's short. But like regexes, you might want to write a good description of what your confusing syntax does for posterity's sake.
Solution
a)
Bash, ksh and zsh support compound conditional (ternary) operators (Bash doesn't do floats, though):
for i in {3..5}; do for j in {2..6}; do for k in {2..4}; do
(( a = i > j ? i > k ? j > k ? i * j : i * k : j * k : 0 ))
echo $a
done; done; done
PHP's ternary operator syntax is similar.
Python's is quite different:
a = b if c else d if e else f if g else h
Bash, et al, and Ruby (which also supports the ?:
form) support this style (showing Bash's version):
[[ condition ]] && do_if_true || do_if_false
which can be done in a compound form. This can perform actions in addition to assignments.
b) no, not without explicit if
/else
(or resorting to even more kludginess creativity.
OTHER TIPS
Actually, C has the ?
operator too, so you could do the same (fugly) thing with C.
However, you are essentially making write-only code there. If you hadn't told me what that awk statement did, it would be very difficult to figure it out. Why not just go ahead and use if
?
You'll thank yourself in 6 months when you discover a need to tweak it.
(a) I think so
(b) Yes
As it happens, your "C" code is almost legal awk. The following slightly tweaked version works just fine as an awk program...
/./ {
price = $1
if (price > 40.00) {
price -= 2
price *= 1.06
} else if ( price > 20.00 && price <= 40.00 ) {
price--
price *= 1.06
} else if ( price > 1.00 ) {
price *= 1.06
}
printf("%6.2f\n", price)
}
I took the semicolons out, but nothing bad happens if you leave them in...