Bash, find the closest next value, forward and backward

I have a data.txt file

1    2     3    4      5       6        7   
cat data.txt
13 245 1323 10.1111 10.2222 60.1111 60.22222
13 133 2325 11.2222 11.333  61.2222 61.3333
13 245 1323 12.3333 12.4444 62.3333 62.44444444
13 245 1323 13.4444 13.5555 63.4444 63.5555

      

Find Closest Nearest: my target value 11.6667

and it should find the closest next value in column 4

like12.3333

Find Previous Closest: my target value 62.9997

and it should find the closest previous value in column 6

like62.3333

I can find the next closest (case 1) on

awk -v c=4 -v t=11.6667 '{a[NR]=$c}END{
        asort(a);d=a[NR]-t;d=d<0?-d:d;v = a[NR]
        for(i=NR-1;i>=1;i--){
                m=a[i]-t;m=m<0?-m:m
                if(m<d){
                    d=m;v=a[i]
                }
        }
        print v
}' f
12.3333

      

Any bash solution? to find the previous closest (case 2)?

+1


source to share


4 answers


Try the following:

$ cat tst.awk
{
    if ($fld > tgt) {
        del = $fld - tgt
        if ( (del < minGtDel) || (++gtHit == 1) ) {
            minGtDel = del
            minGtVal = $fld
        }
    }
    else if ($fld < tgt) {
        del = tgt - $fld
        if ( (del < minLtDel) || (++ltHit == 1) ) {
            minLtDel = del
            minLtVal = $fld
        }
    }
    else {
        minEqVal = $fld
    }
}
END {
    print (minGtVal == "" ? "NaN" : minGtVal)
    print (minLtVal == "" ? "NaN" : minLtVal)
    print (minEqVal == "" ? "NaN" : minEqVal)
}

      



...

$ awk -v fld=4 -v tgt=11.6667 -f tst.awk file
12.3333
11.2222
NaN

$ awk -v fld=6 -v tgt=62.9997 -f tst.awk file
63.4444
62.3333
NaN

$ awk -v fld=6 -v tgt=62.3333 -f tst.awk file
63.4444
61.2222
62.3333

      

+1


source


In the first part:

awk -v v1="11.6667" '$4>v1 {print $4;exit}' file
12.3333

      

And the second part:

awk -v v2="62.9997" '$6>v2 {print p;exit} {p=$6}' file
62.3333

      




Both in one turn:

awk -v v1="11.6667" -v v2="62.9997" '$4>v1 && !p1 {p1=$4} $6>v2 && !p2 {p2=p} {p=$6} END {print p1,p2}' file
12.3333 62.3333

      

+1


source


The solution looks unnecessarily complicated (keeping the whole array and sorting it) and I think you'll see a solution bash

if you change your mind awk

.

In awk

you can locate the first line with

FNR==1 {do something}

      

so on the first line, set the variable BestYet

to the value in the column you are looking for.

On subsequent lines, just check if the value is in the column you are checking,

a) less than your target AND 
b) greater than `BestYet`

      

if so please update BestYet

. At the end, type BestYet

.

In bash

apply the same logic, but to read each line into an array using bash and ${a[n]}

for the n-th element.

0


source


I don't know, this is what you are looking for, but this is what I came up with without knowing awk

:

#!/bin/sh

IFSBAK=$IFS
IFS=$'\n'

best=

for line in `cat $1`; do
    IFS=$' \t'
    arr=($line)


    num=${arr[5]}
    [[ -z $best ]] && best=$num

    if [ $(bc <<< "$num < 62.997") -eq 1 ]; then 
        if [  $(bc <<< "$best < $num") -eq 1 ]; then
            best=$num
        fi
    fi

    IFS=$'\n'
done

IFS=$IFSBAK
echo $best

      

If you want you can add the column and the input value 62.997

as parameters, I haven't demonstrated that it will look for exactly what you want.

Edited to remove file sorting suggestion.

0


source







All Articles