Créer des listes de mots selon des nombres binaires


12

J'ai une matrice qui ressemble à ceci:

Entrée :

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  

Et je voudrais extraire pour chaque ligne la liste des lettres correspondant à la valeur 1.

Sortie :

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

J'ai essayé de diviser l'en-tête et de faire correspondre les mots avec les chiffres, mais j'ai échoué.

Réponses:


12

Dans 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
peut également utiliserNR == 1 { split($0,values) }
Sundeep

C'est sauter la 2ème ligne. Pensez à mettre un nextà la fin de la première ligne afin de ne pas avoir à tester une condition opposée pour les lignes suivantes.
Ed Morton

1
Apparaît que le texte d'entrée original avait une ligne vierge supplémentaire, que j'ai codée. Il a depuis été édité, alors passez NR > 2à NR > 1.
Jeff Schaller

1
Merci pour l'astuce "golf", Sundeep! Je pense que je préfère la boucle explicite «for» car elle s'aligne visuellement / logiquement avec la boucle «for» dans le corps.
Jeff Schaller

1
@ fusion.slope, passez le code entier dans un argument entre guillemets awk, ou collez le code dans un fichier et exécutez-le avecawk -f that.script.file input-file
Jeff Schaller

6

Un autre avec 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
  • -aoption pour diviser la ligne d'entrée sur les espaces blancs, disponible en @Ftableau
  • if($. == 1){ @h=@F } enregistrer l'en-tête si la première ligne
  • @i = grep {$F[$_]==1} (0..$#F) enregistrer l'index si l'entrée est 1
  • print join ",",@h[@i]imprimer uniquement les index du tableau d'en-têtes en utilisant ,comme séparateur

4

Toujours pour le plaisir, une zshversion:

{
   read -A a  &&
   while read -A b; do
     echo ${(j<,>)${(s<>)${(j<>)a:^b}//(?0|1)}}
   done
} < file
  • ${a:^b} zippe les deux tableaux, vous obtenez donc A 0 B 0 C 0 D 0 E 1 F 0 G 0 H 0 I 1
  • ${(j<>)...} joint les éléments sans rien entre eux, il devient donc A0B0C0D0E1F0G0H0I1
  • ${...//(?0|1)}nous en retirons le ?0et 1il devient donc EI:
  • ${(s<>)...} diviser sur rien pour obtenir un tableau d'un élément par lettre: EI
  • ${(j<,>)...}rejoindre ceux avec ,-> E, I.

c'est juste un simple bash non?
fusion.slope

1
@ fusion.slope, non, c'est zshun shell différent de bash(et beaucoup plus puissant, et avec un bien meilleur design si vous me le demandez). basha emprunté une infime fraction de zsh« la fonction de (comme {1..4}, <<<, **/*) pas ceux mentionnés ici, la plupart des bash» caractéristiques de sont par ailleurs empruntés ksh.
Stéphane Chazelas

3

Une autre solution awk :

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

Le résultat:

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

2

Voici une solution en 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";
}

Il fonctionne en lisant les colonnes d'en-tête dans un tableau puis, pour chaque ligne de données, en copiant le nom de la colonne dans un tableau de sortie si la colonne de données correspondante est évaluée comme vraie. Les noms de colonne sont ensuite imprimés séparés par des virgules.


2

Un sedpour le plaisir:

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

Avec GNU sed, vous pouvez le rendre un peu plus lisible avec:

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'

Une version légèrement plus courte, en supposant qu'il y a toujours le même nombre de chiffres sur chaque ligne:

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'

Comme ci-dessus, sauf que nous échangeons les parties traduites et indexées, ce qui permet certaines optimisations.


si vous pouvez expliquer, ce serait bon pour la communauté. Merci d'avance
fusion.slope

1
@ fusion.slope, voir modifier.
Stéphane Chazelas

belle boucle avec la commande 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

Solution pure bash:

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
Veuillez expliquer comment cela résout le problème.
Scott

Cela reste un exercice pour le lecteur. En supposant que les connaissances bash bash LESS="+/^ {3}Array" man bashdevraient fournir toutes les informations nécessaires pour les tableaux bash. Vous êtes libre de modifier la réponse pour ajouter toute clarification utile.
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
Veuillez expliquer ce que cela fait et comment cela fonctionne.
Scott

aussi la langue, s'il vous plaît.
fusion.slope
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.