行から一意にソートする

行から一意にソートする

行または列から一意に並べ替える方法を教えてください。次のような情報があります。

Special   c1,c2,c5,c7,c1,c2   
Special2  C6

Special(との間にあるのは TAB 文字ですc1...)。

次のような出力が欲しいです:

Special   c1,c2,c5,c7  
Special2  C6

どうすればこれを実現できるでしょうか?

答え1

使用この答え

perl -MList::MoreUtils=uniq -laF'\t' -ne '
    $F[1] = join(",", uniq(sort(split(",", $F[1])))); print join("\t", @F)'

これは外部パッケージに依存しますリスト::MoreUtils外部依存関係をインストールしたくない場合は、uniq関数を再実装するとPerlのほんの数行(ただし、macOS では基本システムの一部としてインストールされているようです。)

答え2

perl -F'\t|,' -lane 'my %h; print shift @F, "\t", join ",", sort grep !$h{$_}++, @F' dataf

説明

  • -F'\t|,'@F=> は各レコード フィールドを個TABまたは個の文字の配列に分割しますcomma
  • -lRSnewlineも設定されORSますnewline
  • -aFSは、選択された基準に基づいて各レコードを単語に自動分割します-F
  • -n入力時に暗黙的なレコード読み取りループを設定し、AND要求された場合にのみ印刷します。
  • -e上記で選択した内容Perlに基づいて入力の各レコードに対して実行されるコードです。RS-l
  • 最初の要素は によって指定されshift、残りの要素はuniquifiedによって指定されます。ハッシュ のキーとして格納され%h、レコードが読み込まれるたびに再生成されます。一意の要素はソートされ、カンマで結合されて出力されます。

答え3

OpenBSD awk、GNU awk、およびでテスト済みmawk:

awk -F ',| +' '{ for (i = 2; i <= NF; ++i) { print $1, $i } }' data.in |
sort -u |
awk '{ f[$1] = (f[$1] ? f[$1] "," : "") $2 } END { for (k in f) { print k, f[k] } }'

1つ目はawk与えられたデータを次のように拡張します。

Special c1
Special c2
Special c5
Special c7
Special c1
Special c2
Special2 C6

フィールド区切り文字としてコンマと複数のスペースの両方を使用し、入力の各レコード (行) に対して、最初のフィールドを出力し、その後に他の各フィールドを順番に別々の行に出力します。これは、区切り文字として適切に解釈される場所以外に、行にスペースやコンマがないことを前提としています。

sort真ん中のものはそれを分類します

Special2 C6
Special c1
Special c2
Special c5
Special c7

完全な行をソートキーとして使用してソートを実行し、重複する行を破棄します。

最後にawkデータを結合して

Special c1,c2,c5,c7
Special2 C6

これは、最初のフィールドを連想配列のキーとして使用し、対応するデータをコンマで区切って連結したものを 2 番目のフィールドの値として保存することで行われます。最後に、収集されたすべてのデータが印刷されます。

答え4

もう一つの方法は一行で:

while read line; do echo "$line" | awk '{print $1}' | tr '\n' ' ';  echo "$line" | awk '{print $2}' | tr ',' '\n' | sort -u | tr '\n' ',' | sed -e 's/.$//g'; echo; done < file_to_sort

各行の最初の列 ( echo $line | awk '{print $1}' | tr '\n' ' ';) を取得し、2 番目の列の値を ',' で区切って並べ替え、単一の列に変換してから適用しsort、元の書式で単一の行に戻します ( echo $line | awk '{print $2}' | tr ',' '\n' | sort -u | tr '\n' ',')。

@tripleee の提案に従って行分割を実行します。

while IFS=$'\t' read first second; do printf "%s\t%s\n" "$first" "$(tr ',' '\n' <<<"$second" | sort | tr '\n' ',' | sed -e 's/.$//g';)"; done < file_to_sort

関連情報