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