Buat daftar kata berdasarkan angka biner


12

Saya memiliki matriks yang terlihat seperti berikut:

Masukan :

A   B   C   D   E   F   G   H   I 
0   0   0   0   1   0   0   0   1
0   0   0   1   0   0   0   0   0  
0   0   0   1   0   0   0   0   0  
1   0   0   0   0   0   0   0   0  
1   0   1   0   0   0   1   0   0  
1   0   0   1   0   0   0   1   0  
1   0   0   0   1   1   1   0   0  

Dan saya ingin mengekstrak setiap baris daftar huruf yang sesuai dengan nilai 1.

Keluaran :

E,I 
D
D
A
A,C,G  
A,D,H  
A,E,F,G  

Saya telah mencoba untuk membagi header dan mencocokkan kata-kata dengan angka tetapi saya gagal.

Jawaban:


12

Dalam awk:

NR == 1 { for(column=1; column <= NF; column++) values[column]=$column; }
NR > 1 { output=""
        for(column=1; column <= NF; column++)
                if($column) output=output ? output "," values[column] : values[column]
        print output }

6
bisa juga menggunakanNR == 1 { split($0,values) }
Sundeep

Itu melewatkan baris ke-2. Pertimbangkan untuk meletakkan nextdi akhir baris pertama sehingga Anda tidak perlu menguji kondisi yang berlawanan untuk baris berikutnya.
Ed Morton

1
Muncul teks input asli memiliki baris kosong ekstra di dalamnya, yang saya kodekan. Sejak diedit, jadi ubah saja NR > 2menjadi NR > 1.
Jeff Schaller

1
Terima kasih atas tip "golf", Sundeep! Saya pikir saya lebih suka loop 'for' eksplisit karena berbaris secara visual / logis dengan loop 'for' dalam tubuh.
Jeff Schaller

1
@ fusion.slope, kirimkan seluruh kode dalam argumen yang dikutip tunggal awk , atau tempel kode ke dalam file dan jalankan denganawk -f that.script.file input-file
Jeff Schaller

6

Satu lagi dengan perl

$ perl -lane 'if($. == 1){ @h=@F }
              else{@i = grep {$F[$_]==1} (0..$#F); print join ",",@h[@i]}
             ' ip.txt
E,I
D
D
A
A,C,G
A,D,H
A,E,F,G
  • -apilihan untuk membagi jalur input pada spasi putih, tersedia dalam @Flarik
  • if($. == 1){ @h=@F } simpan tajuk jika baris pertama
  • @i = grep {$F[$_]==1} (0..$#F) simpan indeks jika entri 1
  • print join ",",@h[@i]hanya cetak indeks dari array tajuk menggunakan ,sebagai pemisah

4

Masih untuk bersenang-senang, a zsh versi:

{
   read -A a  &&
   while read -A b; do
     echo ${(j<,>)${(s<>)${(j<>)a:^b}//(?0|1)}}
   done
} < file
  • ${a:^b} ritsleting dua array, sehingga Anda mendapatkan A 0 B 0 C 0 D 0 E 1 F 0 G 0 H 0 I 1
  • ${(j<>)...} bergabung dengan elemen tanpa apa-apa di antaranya sehingga menjadi A0B0C0D0E1F0G0H0I1
  • ${...//(?0|1)} kami menanggalkan ?0 dan 1dari itu sehingga menjadi EI:
  • ${(s<>)...} pisah apa-apa untuk mendapatkan larik satu elemen per huruf: EI
  • ${(j<,>)...}bergabunglah dengan ,-> E, I.

ini hanya sebuah bash sederhana kan?
fusion.slope

1
@ fusion.slope, Tidak, itu zsh, shell yang berbeda dari bash(dan jauh lebih kuat, dan dengan desain yang jauh lebih baik jika Anda bertanya kepada saya). bashtelah meminjam hanya sebagian kecil dari zsh'fitur s (seperti {1..4}, <<<, **/*) bukan yang disebutkan di sini, Sebagian bash' s fitur yang sebaliknya dipinjam dari ksh.
Stéphane Chazelas

3

Lain awk solusi:

awk 'NR==1{ split($0,a); next }   # capture and print `header` fields
     { for (i=1;i<=NF;i++)         # iterating through value fields `[0 1 ...]`
           if ($i) { printf "%s",(f?","a[i]:a[i]); f=1 } 
       f=0; print "" 
     }' file

Hasil:

E,I
D
D
A
A,C,G
A,D,H
A,E,F,G

2

Berikut ini solusi di Perl:

use strict;

my @header = split /\s+/, <>;
<>; ## Skip blank line
while (<>) {
    my @flags = split /\s+/;
    my @letters = ();
    for my $i (0 .. scalar @flags - 1) {
        push @letters, $header[$i] if $flags[$i];
    }

    print join(',', @letters), "\n";
}

Ia bekerja dengan membaca kolom header ke dalam array dan kemudian, untuk setiap baris data, menyalin nama kolom ke array output jika kolom data yang cocok dievaluasi sebagai benar. Nama kolom kemudian dicetak dengan tanda koma.


2

Satu seduntuk bersenang-senang:

sed '
  s/ //g
  1{h;d;}
  G;s/^/\
/
  :1
    s/\n0\(.*\n\)./\
\1/
    s/\n1\(.*\n\)\(.\)/\2\
\1/
  t1
  s/\n.*//
  s/./&,/g;s/,$//'

Dengan GNU sed, Anda dapat membuatnya sedikit lebih terbaca dengan:

sed -E '
  s/ //g # strip the spaces

  1{h;d} # hold the first line

  G;s/^/\n/ # append the held line and prepend an empty line so the
            # pattern space becomes <NL>010101010<NL>ABCDEFGHI we will
            # build the translated version in the part before the first NL
            # eating one character at a time off the start of the
            # 010101010 and ABCDEFGHI parts in a loop:
  :1
    s/\n0(.*\n)./\n\1/     # ...<NL>0...<NL>CDEFGHI becomes
                           # ...<NL>...<NL>DEFGHI (0 gone along with C)

    s/\n1(.*\n)(.)/\2\n\1/ # ...<NL>1...<NL>CDEFGHI becomes
                           # ...C<NL>...<NL>DEFGHI (1 gone but C moved to 
                           #                        the translated part)
  t1 # loop as long as any of those s commands succeed

  s/\n.*// # in the end we have "ADG<NL><NL>", strip those NLs

  s/./,&/2g # insert a , before the 2nd and following characters'

Versi yang sedikit lebih pendek, dengan asumsi selalu ada jumlah digit yang sama di setiap baris:

sed -E '
  s/ //g
  1{H;d}
  G
  :1
    s/^0(.*\n)./\1/
    s/^1(.*\n)(.*\n)(.)/\1\3\2/
  t1
  s/\n//g
  s/./,&/2g'

Sama seperti di atas kecuali kami mengganti bagian yang diterjemahkan dan indeks yang memungkinkan untuk beberapa optimasi.


kalau bisa jelaskan akan baik untuk masyarakat. Terima kasih sebelumnya
fusion.slope

1
@ fusion.slope, lihat edit.
Stéphane Chazelas

bagus loop dengan perintah t1!
fusion.slope

1

python3

python3 -c '
import sys
header = next(sys.stdin).rstrip().split()
for line in sys.stdin:
  print(*(h*int(f) for (h, f) in zip(header, line.rstrip().split()) if int(f)), sep=",")

  ' <file
E,I
D
D
A
A,C,G
A,D,H
A,E,F,G

0

Solusi bash murni:

read -a h
while read -a r
do (
    for i in ${!r[@]}
    do 
        (( r[i] == 1 )) && y[i]=${h[i]}
    done
    IFS=,
    echo "${y[*]}")
done

3
Tolong jelaskan bagaimana ini memecahkan masalah.
Scott

Itu dibiarkan sebagai latihan untuk pembaca. Dengan asumsi pengetahuan dasar bash LESS="+/^ {3}Array" man bashharus memberikan semua informasi yang diperlukan untuk array bash. Anda bebas mengedit jawaban untuk menambahkan klarifikasi yang membantu.
David Ongaro

-1
 void Main(string[] args)
        {
            int[,] numbers = new int[,]
            {
            {0, 0, 0, 0, 1, 0, 0, 0, 1},
            {0, 0, 0, 1, 0, 0, 0, 0, 0},
            {0, 0, 0, 1, 0, 0, 0, 0, 0},
            {1, 0, 0, 0, 0, 0, 0, 0, 0},
            {1, 0, 1, 0, 0, 0, 1, 0, 0},
            {1, 0, 0, 1, 0, 0, 0, 1, 0},
            {1, 0, 0, 0, 1, 1, 1, 0, 0}
            };
            string letters = "ABCDEFGHI";
            for (int row = 0; row < 7; row++)
            {
                for (int col = 0; col < 9; col++)
                {
                    if (numbers[row, col] == 1)
                        Console.Write(letters[col]);
                }
                Console.WriteLine();
            }
        }

3
Tolong jelaskan apa yang dilakukannya dan bagaimana cara kerjanya.
Scott

juga bahasanya.
fusion.slope
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.