2

I have an array like this:

array=(1 2 7 6)

and would like to search for the second largest value, with the output being

secondGreatest=6

Is there any way to do this in bash?

  • 3
    What if array=(1 2 7 6 7)? What is the 2nd largest value, 6 or 7? – glenn jackman Jan 23 '19 at 15:38
4
printf '%s\n' "${array[@]}" | sort -n | tail -2 | head -1

Print each value of the array on it's own line, sort it, get the last 2 values, remove the last value

secondGreatest=$(printf '%s\n' "${array[@]}" | sort -n | tail -2 | head -1)

Set that value to the secondGreatest variable.


Glenn Jackman had an excellent point about duplicate numbers that I didn't consider. If you only care about unique values you can use the -u flag of sort:

secondGreatest=$(printf '%s\n' "${array[@]}" | sort -nu | tail -2 | head -1)
| improve this answer | |
  • 5
    You could save one pipe if you reverse sort, so that the answer is always in second place e.g. printf '%s\n' "${array[@]}" | sort -rn | awk NR==2 (likely head + tail is more efficient though) or (at least with GNU Coreutils, which support the null delimiters) printf '%s\0' "${array[@]}" | sort -rzn | cut -d '' -f2 – steeldriver Jan 23 '19 at 14:03
4

A bash-specific loop through the array could do it; you have to keep track of the largest and second-largest. The only other tricky part is to be careful about initializing those values; the largest value is initialized to the first element; the second-largest value is initialized the first time we see a value that's smaller than the largest value. Subsequently for the second-largest value, we only update it if it's strictly less than the current largest value:

#!/bin/bash

array=(7 7 6 2 1)

if [ "${#array[@]}" -lt 2 ]
then
  echo Incoming array is not large enough >&2
  exit 1
fi

largest=${array[0]}
secondGreatest='unset'

for((i=1; i < ${#array[@]}; i++))
do
  if [[ ${array[i]} > $largest ]]
  then
    secondGreatest=$largest
    largest=${array[i]}
  elif (( ${array[i]} != $largest )) && { [[ "$secondGreatest" = "unset" ]] || [[ ${array[i]} > $secondGreatest ]]; }
  then
    secondGreatest=${array[i]}
  fi
done

echo "secondGreatest = $secondGreatest"

It's still slower than calling out to sort, but it has the added benefit of picking the strictly-smaller second-largest value in the face of multiple high values (such as 7 and 7 above).

| improve this answer | |
0

It's a good job for dc :

array=(1 2 7 6)
echo ${array[*]} | dc -f - -e '
  [lasbdsa]sB
  [dla!>Bsc1z>A]sA
  lAx
  [secondGreatest=]nlbp'
| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.