1

I used this function to find values in an array for a while but I'd like to improve it:

 # Checks if the first argument is found in the subsequent ones.

 function my_function_is_value_in() {

   local -r NEEDLE=$1
   local -ra HAYSTACK=( "${@:2}" )

   local value
   for value in "${HAYSTACK[@]}"; do
     [[ $value == "$NEEDLE" ]] && return 0
   done

   return 1

 }

I now think the assignment to the "parameter renaming for readability" array HAYSTACK is inefficient, especially for a "search" function, potentially run many times as so:

my_function_is_value_in coconut cherry coriander coconut cottage-cheese

Does Bash have an efficient way to do the above or is looping through ${@:2} directly as efficient as this can get?

Compare/contrast with perl's grep( /pattern/ @array).. that's a dedicated function to do exactly this.

1
  • perl's grep function is not efficient for this task. It iterates through the entire array, even if the first element was a match. Never use perl's grep in boolean context, use List::MoreUtils::any. Commented Apr 1, 2013 at 1:56

2 Answers 2

1

You might get better results from this, but I've not done any measurements to demonstrate either way:

function my_function_is_value_in()
{
    local -r NEEDLE=$1
    shift
    local value
    for value in "$@"
    do
        [[ $value == "$NEEDLE" ]] && return 0
    done
    return 1
}

The thinking is that the shell doesn't need to make a copy of the array of arguments this way; that should speed things up.

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

3 Comments

Here an hopefully interesting question for you, Jonathan: are you sure shift does not perform a copy of the positional arguments? Could you prove it? array.c has a array_shift method that works on a null-terminated linked list but builtins/shift.def hints to copying to me (dollar_vars[count] = dollar_vars[count + 1];). Your take?
Hmmm... Q: Are you sure shift does not perform a copy of the positional arguments? A: Moderately. Q: Could you prove it? A: Not without scrutinizing shell source code. However, there's no use for the old value of $1 once it is shifted, and no reason to make a copy of $2 et al. Of course, function arguments are different from script arguments, but the concepts still apply. I've not studied the bash code, and really don't have plans to do so at the moment.
The doubt came to me from looking at this: bashcookbook.com/bashinfo/source/bash-4.1/builtins/shift.def (dollar_vars[count] = dollar_vars[count + 1];) but, as you say, it's not important now.. Thanks for the answer, which I accepted! :)
0

Seems to be faster than an explicit loop:

my_function_is_value_in ()
{
  [[ "${@:2}" =~ "$1" ]] && return 0 || return 1
} 

Comments

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.