Bashから数値範囲で2つの配列を並べ替える

Bashから数値範囲で2つの配列を並べ替える

作業したい要素の数と同じ数の要素を持つ2つの配列があります。ファイルから2つの配列に読み込みます(奇数行は配列1に、偶数行は配列2に移動します)。

arr1=("1" "1" "3" "2" "4" "7" "7" "7" "1" "2" "3" "3" "3" "3" "7" "5")
arr2=("4" "1" "3" "5" "7" "1" "2" "3" "2" "9" "2" "6" "8" "9" "4" "6")

このデータは、2つのアレイの同じ場所にあるシーズンとエピソードの数を表します。したがって、配列1(arr1)はシーズン、配列2(arr2)はエピソードであり、要素番号に基づいてソートされます。等${arr1[0]}に該当します。${arr2[0]}

私がしたいのは、シーズンごとに最初に並べ替えてから、エピソード別に並べ替えることです。したがって、元の配列(どの項目がどの要素であるかコメントを含む)は次のようになります。

       1   2   3   4   5   6   7   8   9   10  11  12  13  14  15  16
arr1=("1" "1" "3" "2" "4" "7" "7" "7" "1" "2" "3" "3" "3" "3" "7" "5")
arr2=("4" "1" "3" "5" "7" "1" "2" "3" "2" "9" "2" "6" "8" "9" "4" "6")
       1   2   3   4   5   6   7   8   9   10  11  12  13  14  15  16

になる:

       2   9   1   4   10  12  3   12  13  14  5   16  6   7   8   15
arr1=("1" "1" "1" "2" "2" "3" "3" "3" "3" "3" "4" "5" "7" "7" "7" "7")
arr2=("1" "2" "4" "5" "9" "2" "3" "6" "8" "9" "7" "6" "1" "2" "3" "4")
       2   9   1   4   10  12  3   12  13  14  5   16  6   7   8   15

考えられる考え:

  1. iの各エントリに対して、対応する${arr1[@]}要素が${arr2[n]}ファイルに書き込まれます。その後、ファイルをsort実行できます。
n="0"
for i in "${arr1[@]}"; do
    echo "${arr2[${n}]}" >> "${i}.txt"
    (( n++ ))
done

しかし、私はディスクへの書き込みが含まれないようにしたいと思います(sort必要ない場合)。

  1. データを一種の別々の配列にソートしますか?各季節には独自の配列があり、同様の方法でソートできますが、sort -n "${season1Arr[@]}"これがどのように実行されるのかわかりません。

  2. データ処理方法を変更しますか?入力ファイルは変更できませんが、処理方法は変更できます。偶数/奇数行番号に基づいて、2つの配列で行を読み取るのではなく、別の方法で管理できますか?

互換性のためにできるだけ純粋なbashを維持しようとしていますが、外部プログラムを使用する必要がある可能性が高いことを知っています。どんなアイデアもありがとうございます。

答え1

以下は、コメントで説明した例です。 Perlと連想配列(ハッシュ)を使用しますが、私が言及した自然順序(単純な数字または英数字の順序、たとえば1.10より前)の代わりにバージョン順序を使用します。 1.2、つまり「series.episode」は次のようになります。明らかに間違っています):

#!/usr/bin/perl

use strict;

use Sort::Versions;

my %data;
my ($key, $series, $episode);

while (<>) {
  chomp; # remove trailing newline from input

  if ($. % 2 == 1) {
    $series = $_;
  } else {
    $episode = $_;
    $key = "$series.$episode";
    $data{$key} = 1;
  };
}

print join(", ", sort { versioncmp($a, $b) } keys %data), "\n";

これは少し短くなるかもしれません。実際に必要な唯一の変数は、入力値の使用方法を明確にするために使用された%dataハッシュです。 BTWはデフォルトの入力/値/反復子です。これはPerlで多くの目的に使用され、他の変数が提供されていない場合は、多くの関数と構文要素がそれを使用します。このスクリプトでは、ループが読み取る現在の行の値です。 「一般変数」を表示して検索します。$series$key$episode$_while (<>)man perlvar

#!/usr/bin/perl

use strict;
use Sort::Versions;

my (%data, $series);

while (<>) {
  chomp; 

  if ($. % 2 == 1) {
    $series = $_;
  } else {
    $data{"$series.$_"} = 1;
  };
}

print join(", ", sort { versioncmp($a, $b) } keys %data), "\n";

実行例(両方のバージョンが同じ出力を生成します):

$ ./sort-series-episode.pl input.txt  
1.1, 1.2, 1.4, 1.15, 2.5, 2.9, 3.2, 3.3, 3.6, 3.8, 3.9, 4.7, 5.6, 7.1, 7.2, 7.3, 7.4

メモ:並べ替え::バ​​ージョンモジュールはコアPerlモジュールではなく、配布パッケージを介してcpan個別にインストールする必要があります。たとえば、Debianではapt-get install libsort-versions-perl

答え2

perlこの種の作業ははるかに優れていますが、純粋なbashでほとんど行うことができます(外部使用に安全です)sort

#!/bin/bash
arr1=( "1" "1" "3" "2" "4" "7" "7" "7" "1" "2" "3" "3" "3" "3" "7" "5" )
arr2=( "4" "1" "3" "5" "7" "1" "2" "3" "2" "9" "2" "6" "8" "9" "4" "6" )

# make an array with strings from both initial arrays:
arr_length=${#arr1[@]}
for ((i=0; i< $arr_length; i++)); do
   arr_combined[$i]="${arr1[$i]}!${arr2[$i]}"
done

# sort the combined strings
arr_sorted=( $(printf "%s\n" "${arr_combined[@]}" | sort) )

# split the elements of sorted array back into two arrays
for pair in ${arr_sorted[@]} ; do
    arr1n+="${pair%!*} "
    arr2n+="${pair#*!} "
done

# print the results
printf "%s\n" "${arr1n[@]}"
printf "%s\n" "${arr2n[@]}"

答え3

元のファイルの番号を最初にソートしてから配列を埋めると、作業が簡単になります。元のファイルに次の内容が含まれているとします。

$ cat file1
1 1 3 2 4 7 7 7 1 2 3 3 3 3 7 5
4 1 3 5 7 1 2 3 2 9 2 6 8 9 4 6

したがって、このコマンドは次のようになります。

transpose file1 | sort | transpose

あなたが望むものを生成します:

1 1 1 2 2 3 3 3 3 3 4 5 7 7 7 7
1 2 4 5 9 2 3 6 8 9 7 6 1 2 3 4

転移機能は非常に簡単です。自分で書くこともできます。例は次のとおりです。

transpose () 
{ 
    awk '{ for (i=1; i<=NF; i++) a[i]= (i in a?a[i] OFS :"") $i; } 
    END{ for (i=1; i<=NF; i++) print a[i] }' $1
}

答え4

あなたの説明によると、以下は元の入力データかもしれません:

1
4
1
1
3
3
2
5
4
7
7
1
7
2
7
3
1
2
2
9
3
2
3
6
3
8
3
9
7
4
5
6

必要な方法でこれを処理するには、次の手順を実行します。

paste - - <file | sort -k 1,1n -k 2,2n

まず、データをタブ区切りの2つの列に変換します。最初の列はシーズンで、2番目の列はエピソードです。

行は最初の列に数字でソートされ、最初の列が同じ行は2番目の列に数字でソートされます。

上記の入力を使用すると、次のものが生成されます。

1       1
1       2
1       4
2       5
2       9
3       2
3       3
3       6
3       8
3       9
4       7
5       6
7       1
7       2
7       3
7       4

関連情報