6

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?

George Stocker
  • 57,289
  • 29
  • 176
  • 237
Dan-Simon Myrland
  • 327
  • 1
  • 5
  • 10
  • Not correct! Try `"${@:2}"`. – TrueY Jun 11 '13 at 10:51
  • Very similar to array intersection questions [1](http://stackoverflow.com/questions/2696055/intersection-of-two-lists-in-bash) and [2](http://stackoverflow.com/questions/7870230/array-intersection-in-bash). – l0b0 Jun 11 '13 at 10:59
  • 2
    To show the question was solved, tick one of the answers as "accepted". – fedorqui Jun 11 '13 at 13:03

4 Answers4

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
TrueY
  • 7,360
  • 1
  • 41
  • 46
  • @l0b0: You are right! The first function handled it badly. I corrected. Thanks! – TrueY Jun 11 '13 at 11:34
  • @l0b0: I just wanted to upvote your last comment, but it was removed... ;) Anyway thanks for them! – TrueY Jun 11 '13 at 11:47
  • This fails when I try it. It returns nothing, no matter what. – Dan-Simon Myrland Jun 11 '13 at 12:03
  • @Dan-SimonMyrland: Sorry, I overreacted my last correction! Bad single quotation marks were removed. Corrected! Thanks! – TrueY Jun 11 '13 at 12:39
  • Just FYI: Associative arrays are [supported by Bash 4.0-alpha and newer](http://wiki.bash-hackers.org/scripting/bashchanges). – l0b0 Jun 11 '13 at 13:46
  • @l0b0: Yepp. If You have older, try `ArrContainsRe` version... it is hashless. :) – TrueY Jun 11 '13 at 13:49
  • Except `ArrContainsRe` won't work if your separator is in the string. I prefer solutions which work no matter the input. – l0b0 Jun 11 '13 at 13:52
  • @l0b0: Some non-printing character might be used as separator. – TrueY Jun 11 '13 at 14:00
  • @TrueY And your input could still contain that character. The point is that there is absolutely no character (or sequence of characters) which you can use as a separator which can't also be part of the input. – l0b0 Jun 11 '13 at 14:02
  • Late for the party, but I think name-refs can be used in Bash 4.3+ to make array-access nicer. Here, one would declare lArr1 like this: `declare -n lArr1="$1"`, removing the need for the nasty (and potentially dangerous) `eval`. – Timor Gruber May 11 '20 at 10:09
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
Daniel
  • 191
  • 3
  • 5
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 :)

Dan-Simon Myrland
  • 327
  • 1
  • 5
  • 10
  • 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. – TrueY Jun 11 '13 at 12:47
  • Ok, your own solution proved a better way of solving my problem. Thanks again for your help! – Dan-Simon Myrland Jun 11 '13 at 13:02
-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.

l0b0
  • 55,365
  • 30
  • 138
  • 223
  • How would you take two arrays as parameters? It's not possible. – l0b0 Jun 11 '13 at 11:02
  • You can take the size of the first array as the first parameter :-) – choroba Jun 11 '13 at 11:02
  • "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. – l0b0 Jun 11 '13 at 11:03
  • Can you please delete the comment in that case? It's just noise now :/ – l0b0 Jun 11 '13 at 11:22
  • I removed the comment, but a half of it was true: the function does not take parameters. – choroba Jun 11 '13 at 11:49
  • @choroba: IMHO in `bash` it is ok to use implicit parameters. – TrueY Jun 11 '13 at 13:17
  • @TrueY: I did not say it is not ok. It is just a different behaviour compared to the original function. – choroba Jun 11 '13 at 13:19
  • 1
    @choroba: Exactly the same cannot be implemented. Some trick has to be added as `bash` cannot pass two separate arrays to a function. – TrueY Jun 11 '13 at 13:47