Ahmad
Ahmad

Reputation: 9708

How to get the index of an element in an array using a bash function

This is my code:

function get_index() {
 my_array="$1"
 value="$2"
 for i in "${!my_array[@]}"; do
   if [[ "${my_array[$i]}" = "${value}" ]]; then
       echo "was found"
       echo "${i}";
   fi
 done
 return -1
}

echo "index test"
get_index "${cut_pages[@]}" 5
index=$?
echo $index

The array includes 2 and 5 It must return 1, but it returns 255

What's the problem?

Upvotes: 1

Views: 1673

Answers (3)

Léa Gris
Léa Gris

Reputation: 19685

Expanding on other answers, here are featured functions to handle a value at multiple indexes; and to handle multiples values at multiple indexes, with returning result to an array:

#!/usr/bin/env bash

function get_indexes() {
  local -n my_array="$1" # use -n for a reference to the array
  local -a indx=()
  local -i rc=1
  for i in "${!my_array[@]}"; do
    if [ "${my_array[i]}" = "$2" ]; then
      indx+=("$i")
      rc=0
    fi
  done
  echo "${indx[*]}"
  return $rc
}

function get_all_indexes() {
  if [ $# -lt 3 ]; then
    printf 'get_all_indexes input_array output_array values...\n' >&2
    return 2
  fi
  local -n input_array="$1" # use -n for a reference to the array
  shift
  local -n output_array="$1"
  shift
  if ! [[ "$(typeset -p "${!output_array}")" =~ ^declare\ -A ]]; then
     printf 'Output array %s must be an associative array!\n' "${!output_array}" >&2
     return 2
  fi

  local -i rc=1
  for i in "${!input_array[@]}"; do
    for value; do
      if [ "${input_array[$i]}" = "$value" ]; then
        if [ ${#output_array[$value]} -eq 0 ]; then
          output_array[$value]="$i"
        else
          printf -v output_array[$value] $'%s\3%s' "${output_array[$value]}" "$i"
        fi
        rc=0
      fi
    done
  done
  return $rc
}

echo "Testing multi indexes single value"
declare -a cut_pages=([3]=2 [7]=5 [1]=3 [0]=5)
typeset -p cut_pages
if indexes=$(get_indexes cut_pages 5); then
  echo "found 5 at indexes:"
  echo "$indexes"
fi

printf "\n\nTesting multi indexes multiple values output to array\n"
declare -A out_arr=()
declare -A in_arr=(
 ["first"]="hello"
 ["second"]="world"
 ["third"]="hello"
 ["fourth"]="you"
)
typeset -p in_arr

if get_all_indexes in_arr out_arr "hello" "world" "you"; then
  for k in "${!out_arr[@]}"; do
    printf 'Found %q at indexes:\n' "$k"
    IFS=$'\3' read -r -a indexes <<<"${out_arr[$k]}"
    printf '%q\n' "${indexes[@]}"
  done
fi

Output result:

Testing multi indexes single value
declare -a cut_pages=([0]="5" [1]="3" [3]="2" [7]="5")
found 5 at indexes:
0 7


Testing multi indexes multiple values output to array
declare -A in_arr=([fourth]="you" [third]="hello" [first]="hello" [second]="world" )

Found world at indexes:
second
Found you at indexes:
fourth
Found hello at indexes:
third
first

Upvotes: 2

Freddy
Freddy

Reputation: 4718

You could use a name reference to the array instead of passing the whole array as individual arguments:

function get_index() {
  local -n my_array=$1 # use -n for a reference to the array
  for i in "${!my_array[@]}"; do
    if [[ ${my_array[i]} = $2 ]]; then
      printf '%s\n' "$i"
      return
    fi
  done
  return 1
}

cut_pages=( 1 2 3 4 5 )
index=$(get_index cut_pages 5) && echo "index=$index"

Upvotes: 3

Barmar
Barmar

Reputation: 782785

You can't pass an array as a single argument to a function. When you write "${cut_pages[@]}" it spreads the array into separate arguments to the function.

I've changed the function to take the value to search for as the first argument, and the array is all the rest of the arguments. After assigning this to value, the shift command removes it from the argument list, and then I iterate over "$@" to process the remaining arguments.

You're also not getting the result of the function correctly. $? contains the status code in the return statement. To get what the function echoes, you use $(...). I've removed echo "was found" so this won't get into the result.

#!/bin/bash

function get_index() {
    value="$1"
    shift
    local index=0
    for i in "$@"; do
        if [[ "$i" = "${value}" ]]; then
            echo "${index}";
            return
        fi
        ((index++))
    done
    return 1
}

echo "index test"
cut_pages=(2 5)
if index=$(get_index 5 "${cut_pages[@]}"); then
    echo $index
fi

Upvotes: 2

Related Questions