7

I came over this cool Bash function for checking if an array contains an element:

CONTAINS_ELEMENT(){
  local e
  for e in "${@:2}"; do [[ "$e" == "$1" ]] && return 0; done
  return 1
}

Here is an example of it's usage:

if CONTAINS_ELEMENT $element "${array[@]}"; then
... 
fi

My question is this: Is there a way to rewrite this function so that it can check if any value within an array is equal to any value withing the other array, and not just check for one single value as it corrently does?

3
  • Not correct! Try "${@:2}". Commented Jun 11, 2013 at 10:51
  • Very similar to array intersection questions 1 and 2. Commented Jun 11, 2013 at 10:59
  • 2
    To show the question was solved, tick one of the answers as "accepted". Commented Jun 11, 2013 at 13:03

4 Answers 4

3

CORRECTED#3

Try code bellow. ArrContains take two arguments, the name of the two arrays. It creates a temporary hash from lArr1 and then check if any elements of lArr2 is in the hash. This way the embedded for-loops can be avoided.

#!/usr/bin/bash

ArrContains(){
  local lArr1 lArr2
  declare -A tmp
  eval lArr1=("\"\${$1[@]}\"")
  eval lArr2=("\"\${$2[@]}\"")
  for i in "${lArr1[@]}";{ ((++tmp['$i']));}
  for i in "${lArr2[@]}";{ [ -n "${tmp[$i]}" ] && return 0;}
  return 1
}

arr1=("a b" b c)
arr2=(x 'b c' e)
arr3=(q a\ b y)
ArrContains arr1 arr2 && echo Contains arr1 arr2
ArrContains arr1 arr3 && echo Contains arr1 arr3

Output:

Contains arr1 arr3

Other way could be to define some separation character and concatenate the first hash. Then search for matching the SEPitemSEP string.

ArrContainsRe(){
  local lArr1 lArr2 tmp
  eval lArr1=("\"\${$1[@]}\"")
  printf -v tmp ",%s" "${lArr1[@]}";
  tmp="$tmp,"
  eval lArr2=("\"\${$2[@]}\"")
  for i in "${lArr2[@]}";{ [[ $tmp =~ ,$i, ]] && return 0;}
  return 1
}
...
ArrContainsRe arr1 arr2 && echo ContainsRe arr1 arr2
ArrContainsRe arr1 arr3 && echo ContainsRe arr1 arr3

Output:

ContainsRe arr1 arr3
Sign up to request clarification or add additional context in comments.

10 Comments

@l0b0: You are right! The first function handled it badly. I corrected. Thanks!
@l0b0: I just wanted to upvote your last comment, but it was removed... ;) Anyway thanks for them!
This fails when I try it. It returns nothing, no matter what.
@Dan-SimonMyrland: Sorry, I overreacted my last correction! Bad single quotation marks were removed. Corrected! Thanks!
Just FYI: Associative arrays are supported by Bash 4.0-alpha and newer.
|
1

Loop inside loop:

#!/bin/bash
clear

arrA=("a" "b" "c" "d" "e" "f")
arrZERO=("c" "e") # must be turned to "0"

echo "arrA:"
echo ${arrA[@]}
echo ""
echo "arrZERO:"
echo ${arrZERO[@]}

for (( i=0; i < ${#arrA[@]}; i++ ))
do
    for (( j=0; j < ${#arrZERO[@]}; j++ ))
    do
        if [[ ${arrA[$i]} = ${arrZERO[$j]} ]]; then
            arrA[$i]="0"
        fi
    done
done

echo ""
echo "FINAL"
echo ${arrA[@]}

Terminal will show:

arrA:
a b c d e f

arrZERO:
c e

FINAL
a b 0 d 0 f

Comments

0

Looking at the links above I came over a solution that almost does what I need it to do, but not quite:

parameters=($1 $2 $3 $4)
arg1=(h help -h --help)

PARAMETERS(){
pr1=" $parameters[@]} "
for item in ${@:1}; do
  if [[ $pr1 =~ " $item " ]]; then
    return 0
  else
    return 1
  fi
done
}

if PARAMETERS "${arg1[@]}"; then
  echo "It works!"
else
  echo "Nope, still not working..."
fi

Now this code works when passing the parameter "h", but not when passing the other parameters in the array (-h --help help). Sorry if I am doing some stupid mistake here, I am somewhat of a nuub when it comes to bash scripting :)

2 Comments

One syntax problem: pr1=" $parameters[@]} " might be pr1=" ${parameters[@]} ". But I may suggest to use local pr1=.... And a semantics problem: the if will return in the first loop in PARAMETERS. I assume the else part is not needed.
Ok, your own solution proved a better way of solving my problem. Thanks again for your help!
-1

This should do it:

any_overlap() {
    for e1 in "${array1[@]}"
    do
        for e2 in "${array2[@]}"
        do
            if [[ "$e1" = "$e2" ]]
            then
                return 0
            fi
        done
    done
    return 1
}

Test session:

$ array1=(foo bar baz) array2=(ban bat bar) && any_overlap; echo $?
0
$ array1=(foo bar baz) array2=(ban bat) && any_overlap; echo $?
1
$ array1=() array2=() && any_overlap; echo $?
1

Of course, there are faster ways to do it if the arrays are sorted or none of the array elements contain whitespace.

8 Comments

How would you take two arrays as parameters? It's not possible.
You can take the size of the first array as the first parameter :-)
"Also, if the first two elements in the respective arrays are different, it returns 0." What do you mean? I just tested it, and it works fine.
Can you please delete the comment in that case? It's just noise now :/
@choroba: Exactly the same cannot be implemented. Some trick has to be added as bash cannot pass two separate arrays to a function.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.