次のリストがあります。
1 2 5 2
1 5 5 3
1 5 5 5
5 2 2 2
2 2 4 3
各行を並べ替えて、次の一意の値を取得したいと思いますsort | uniq
。
1 2 5
1 3 5
1 5
2 5
2 3 4
オンラインでソリューションを探していますが、列で並べ替えるソリューションのみを見つけることができます。どのように出力を取得できますか?よろしくお願いします。
答え1
行内の列よりも行をソートする方が簡単なので、1つのアプローチは各行を転置して(各フィールドが行になるように)適用してから転置するsort
ことuniq
です。
以下は、GNUツールを想定した簡単な実装です。
$ while read -r line; do echo "$line" | grep -o '[^ ]*' | sort -h | uniq | paste -s; done <file
file
各行に対してループを介して、次の操作を行います。
grep
-o
オプション(各行の一致する部分のみを印刷)を使用して入力を次に分割します。N一致する部分文字列ごとに1つずつ。ここでは、スペースを除くすべての項目を一致させます。- 分割線は、
-h
人間が読める数字を比較するオプションを使用してソートされます(フィールドを英数字文字列でソートするには、このオプションを削除します-h
)。 - この
uniq
コマンドは重複した項目を削除します。 paste -s
標準入力の各行を、タブ区切りの単一行フィールドとして印刷します。末尾の| tr '\t' ' '
文字を追加してタブを空白に変更できます。
しかし、ループを使用してテキストを処理することは通常悪い習慣と考える。
答え2
以下は、複数の列にわたってデータを並べ替えるのではなく、一意の値のみを抽出します。ソートが必要かどうかは不明です。
使用awk
:
$ awk '{ n=split($0,a,FS); $0=""; j=1; delete u; for (i=1; i<=n; i++) if (!u[a[i]]++) $(j++) = a[i]; print }' <file
1 2 5
1 5 3
1 5
5 2
2 4 3
このプログラムは、コメントと一緒に見事に構成されています。
{
# split the current record into fields in the array a
n = split($0, a, FS)
# empty the current record
$0=""
# j is the next field number that we are to set
# in the record that we are building
j=1
# seen is an associative array that we use to
# keep track of whether we've seen a bit of
# data before from this record
delete seen
# loop over the entries in a (the original
# fields of the input data)
for (i=1; i<=n; i++)
# if we haven't seen this data before,
# mark it as seen and...
if (!seen[a[i]]++)
# add it to the j:th field in the new record
$(j++) = a[i]
print
}
ここで私の考えは、元のデータの一意のフィールドを含む各入力行の出力レコードを作成することです。
デフォルトでは、「record」は「row」と同義語、「field」は「column」と同義語です(これは、およびの現在の値に応じてより一般的な単語にすぎませんRS
)FS
。
答え3
パールの使用:
perl -MList::Util=uniq -alne 'print join " ", sort { $a <=> $b } uniq @F' file
1 2 5
1 3 5
1 5
2 5
2 3 4
答え4
bash
@fra-sanのアプローチに似た別のアプローチです。
while read X;do tr<<<$X ' ' \\n|sort -u|paste -sd" ";done<file
1 2 5
1 3 5
1 5
2 5
2 3 4