Qwerty
Qwerty

Reputation: 227

sort based on predefined list

How can I sort the array order based on the list I predefined in Bash.

The list I predefined

fixed=(john doe mary ben)

The array I want it to change

changetimetotime=(ben-5 doe-1 john-1)

There are few criteria I need to meet

The bash code I need

  1. The quantity of changetimetotime array not necessary have 3, sometimes is 1 value but the maximum is 4 value.

  2. fixed array only provide name, changetimetotime provide version number too.

Once both criteria have been met, the array will change to below

changetimetotime=(john-1 doe-1 ben-5) 

And I need to access the changetimetotime array outside of the loop.

Please help.

Upvotes: 0

Views: 47

Answers (1)

Nahuel Fouilleul
Nahuel Fouilleul

Reputation: 19315

If elements are always included in predefined set using a sparse array

fixed=(john doe mary ben)

# to fill an associative array from fixed array
# if supported (bash >4) otherwise use a decoding function
declare -A hash=()
for i in "${!fixed[@]}"; do hash[${fixed[i]}]=$i; done

changetimetotime=(ben-5 doe-1 john-1)
newchangetimetotime=()
for element in "${changetimetotime[@]}"; do
    index=${hash[${element%%-*}]}
    newchangetimetotime[index]=$element
done

echo "${newchangetimetotime[@]}"

# overwrite array reindexing keys
changetimetotime=("${newchangetimetotime[@]}")

Otherwise, general case using a quicksort

# an associative array to decode names to an int
declare -A hash=([john]="1" [doe]="2" [mary]="3" [ben]="4")

# or to fill from fixed array
# declare -A hash=()
# for i in "${!fixed[@]}"; do hash[${fixed[i]}]=$i; done

# function used to compare two elements
# exit status is success (0) if first is less or equal to second
is_ordered () {
    # if string after name can contain many '-', % may be changed to %% to remove logest matched suffix
    # extract names from elements
    local k1=${1%-*} k2=${2%-*}
    local v1=${hash[$k1]} v2=${hash[$k2]}
    (($v1<=$v2))
}

# recursive quick sort
qsort () {
    local l=() g=() p=$1
    r=()
    shift || return
    for i; do
        if is_ordered "$i" "$p"; then
            l+=("$i")
        else
            g+=("$i")
        fi
    done

    qsort "${g[@]}"
    g=("${r[@]}")
    qsort "${l[@]}"
    l=("${r[@]}")

    r=("${l[@]}" "$p" "${g[@]}")
}

qsort "${changetimetotime[@]}"
changetimetotime=("${r[@]}")

Upvotes: 1

Related Questions