24

With Perl you can check if an array contains a value

$ perl -e '@foo=(444,555,666); print 555 ~~ @foo ? "T" : "F"'
T

However with awk, this similar command is checking the array indexes rather than values

$ awk 'BEGIN {split("444 555 666", foo); print 555 in foo ? "T" : "F"}'
F

How can I check if an array contains a particular value with awk?

codeforester
  • 39,467
  • 16
  • 112
  • 140
Zombo
  • 1
  • 62
  • 391
  • 407
  • 1
    I believe your only option is to loop. – Etan Reisner Nov 04 '14 at 22:40
  • 4
    Like Etan says, you need a for-loop. You can achieve a somewhat flexible result by creating a new array (hash) with the values as keys, e.g.: `awk 'BEGIN { split("444 555 666", foo); for(i=1; i<=length(foo); i++) bar[foo[i]]; print 555 in bar ? "T" : "F" }'` – Thor Nov 04 '14 at 23:19
  • 3
    @Thor - Can't you just use `for( i in foo ) bar[foo[i]]` instead? – n0741337 Nov 04 '14 at 23:24
  • 3
    @n0741337: You are right, shorter and cleaner, so it becomes: `awk 'BEGIN { split("444 555 666", foo); for(i in foo) bar[foo[i]]; print 555 in bar ? "T" : "F" }'`. – Thor Nov 04 '14 at 23:27

5 Answers5

28

Awk noob here. I digested Steven's answer and ended up with this hopefully easier to understand snippet below. There are 2 more subtle problems:

  • An Awk array is actually a dictionary. It's not ["value1", "value2"], it's more like {0: "value1", 1: "value2"}.
  • in checks for keys, and there is no built-in way to check for values.

So you have to convert your array (which is actually a dictionary) to a dictionary with the values as keys.

BEGIN {

    split("value1 value2", valuesAsValues)
    # valuesAsValues = {0: "value1", 1: "value2"}

    for (i in valuesAsValues) valuesAsKeys[valuesAsValues[i]] = ""
    # valuesAsKeys = {"value1": "", "value2": ""}
}

# Now you can use `in`
($1 in valuesAsKeys) {print}

For one-liners:

echo "A:B:C:D:E:F" | tr ':' '\n' | \
awk 'BEGIN{ split("A D F", parts); for (i in parts) dict[parts[i]]=""}  $1 in dict'
Thamme Gowda
  • 11,249
  • 5
  • 50
  • 57
phunehehe
  • 8,618
  • 7
  • 49
  • 79
0

what I do is structure my data to take advantage of what the language provides.

So instead of saying :

BEGIN {split("444 555 666", foo); .... }

say :

BEGIN { bar[ "444" ] ; ... }

or ala another suggestion:

BEGIN { 
  split("444 555 666", foo);
  for( i in FOO ) bar[foo[i]];
}

remember arrays in awk are really associative arrays.

Bob Makowski
  • 305
  • 2
  • 8
0

gawk 'BEGIN { split("a b c d",parts); for (i in parts) dict[parts[i]]=1 } $1 in dict {print}' some.log

Ryan Liu
  • 499
  • 4
  • 5
  • While this code may solve the question, [including an explanation](//meta.stackexchange.com/q/114762) of how and why this solves the problem would really help to improve the quality of your post, and probably result in more up-votes. Remember that you are answering the question for readers in the future, not just the person asking now. Please [edit] your answer to add explanations and give an indication of what limitations and assumptions apply. – Yunnosch Sep 08 '22 at 21:07
0
BEGIN {
  split("value1 value2 value3", valueArray)
  # valueArray is {1: "value1", 2: "value2", 3: "value2"}
}
{
  # given $1 is the value to test for
  for (i in valueArray) {
    if ( $1 == valueArray[i] ) {
      print $1
      # OR 
      print "True"
      # ...
    }
  }
}
NicolasK
  • 31
  • 5
0

In the spirit of a one-liner (see Thamme's answer) that sticks more to the OP's perl reference (ternary operator):

echo -e "a\nb\nc" | awk '{t[$1]}END{print (("a" in t) ? "yes":"no")}' # prints yes, try with ("m" in t)

ExpertNoob1
  • 900
  • 1
  • 8
  • 17