値間の明示的な範囲

値間の明示的な範囲

bash魔法を使用してテキストファイルの2番目の列要素間の範囲を計算するきちんとした方法はありますか? (私は現在Pythonを使ってこれを行っています。)

入力:ファイル1

A   1-5
A   17-19
B   1-5
B   4-6

期待される出力:ファイル2

A   1,2,3,4,5,17,18,19
B   1,2,3,4,5,6    

編集する@Anthon:要素を蓄積するために、次のようなものを使用しています。次に、forループを使用して範囲を計算します。

d_pos= {} 
for row in open('File.txt'): 
    x, y = [ value.strip() for value in row.split('\t')] 
    if x in d_pos:        
        d_pos[x].append(y)    
    else:        
        d_pos[x] = [y]

答え1

あなたが要求したように直接bashを使用します(しかし私はbash 4.0が必要な連想配列を使用しています)。

秘訣は中かっこシーケンスの拡張{x..y}、整数xの場合、yはすべて含まれている値の範囲([x、y]など)のテキストリストに展開されます。eval変数拡張の前に中かっこ拡張が発生するため、1つも追加する必要があります。

declare -A data seen  # explicit associative arrays
while read col range; do
   data[$col]="${data[$col]} $(eval echo {${range/-/..}})"
done <<DATA
A   1-5
A   17-19
B   1-5
B   4-6
DATA

# dump array
#declare -p data

for ii in ${!data[@]}; do
    seen=();  datum=""
    # build list of unique values
    for dd in ${data[$ii]}; do
        (( ${seen[$dd]:-0} )) || datum="$datum $dd"
        let seen[$dd]++
    done

    datum=${datum# }     # drop leading space
    datum=${datum// /,}  # spaces to commas
    printf "%-4s %s\n" "$ii" "$datum"
done

シーケンス拡張のバリエーションは、a{x..y}b拡張の各項目の前に「a」を追加し、「b」を追加することです。これを使用して「,」を追加し、必要に応じてデータ変数を変更できます。シーケンス拡張は1の増分を処理し、x> yの場合は-1を処理します。

出力をソートする必要があるかもしれません。反復連想配列のキーには明確に定義された順序がなく、入力範囲が事前にソートされているかどうかを明らかにしませんでした(したがって、コードが複雑すぎませんでした)。

答え2

たとえば、Pythonコードは近いですが、アイテムBの4と5が重なっていることを処理できません。

以下は、重複を防ぐためにaを使用し、入力行にキーがすでに存在する場合は、明示的なテストを削除するためにsetdefaultを使用して文字への依存を減らし、明示的なset()テストを削除することによって問題を正しく処理します。d_pos.split()\t.strip()

d_pos= {}
for row in open('File.txt'):
    x, y = [ value for value in row.split()]
    y1, y2 = map(int, y.split('-'))
    d_pos.setdefault(x, set()).update(range (y1, y2+1))
for x in sorted(d_pos):
    print '{}\t{}'.format(x, ','.join(map(str, d_pos[x])))

答え3

利用可能な場合perl

$ perl -MList::MoreUtils=uniq -anle '
    ($s,$e) = split "-", $F[1];
    push @{$h{$F[0]}}, $s..$e; 
    END {
        $" = ",";
        print "$_   @{[uniq@{$h{$_}}]}" for keys %h;
    }
' file
A   1,2,3,4,5,17,18,19
B   1,2,3,4,5,6

List::MoreUtilsコアにないので使用したくない場合は、次のことができます。

$ perl -anle '
    ($s,$e) = split "-", $F[1];
    push @{$h{$F[0]}}, $s..$e; 
    END {
        $" = ",";
        for $k (keys %h) {
            %u=();
            print "$k   @{[grep {!$u{$_}++} @{$h{$k}}]}";
        }
    }
' file

関連情報