Oneliner para mesclar linhas com o mesmo primeiro campo

15

Esta é a minha primeira pergunta sobre codegolf, por isso peço desculpas antecipadamente, se não for apropriado, e agradeço qualquer feedback.

Eu tenho um arquivo com este formato:

a | rest of first line
b | rest of second line
b | rest of third line
c | rest of fourth line
d | rest of fifth line
d | rest of sixth line

O conteúdo real varia, assim como o delimitador. O conteúdo é apenas texto. O delimitador aparece apenas uma vez por linha. Para este quebra-cabeça, fique à vontade para alterar o delimitador, por exemplo, use "%" como delimitador.

Saída desejada:

a | rest of first line
b | rest of second line % rest of third line
c | rest of fourth line
d | rest of fifth line % rest of sixth line

Eu já tenho scripts ruby ​​e awk para mesclar isso, mas suspeito que é possível ter um pequeno oneliner. isto é, uma linha que pode ser usada junto com pipes e outros comandos na linha de comando. Não consigo descobrir, e meu próprio script é muito longo para compactar na linha de comando.

Caracteres menores preferidos. A entrada não é necessariamente classificada, mas estamos interessados ​​apenas em mesclar linhas consecutivas com os primeiros campos correspondentes. Existem linhas ilimitadas com os primeiros campos correspondentes. O campo 1 pode ser qualquer coisa, por exemplo, nomes de frutas, nomes próprios, etc.

(Eu corro no MacOS, por isso estou pessoalmente mais interessado em implementações que são executadas no mac).


Aqui está um segundo exemplo / teste. Observe "|" é o delimitador. O espaço antes do "|" é irrelevante e se o ressentimento deve ser considerado parte da chave. Estou usando "%" como delimitado na saída, mas, novamente, sinta-se à vontade para alterar o delimitador (mas não use colchetes).

Entrada:

why|[may express] surprise, reluctance, impatience, annoyance, indignation
whom|[used in] questions, subordination
whom|[possessive] whose
whom|[subjective] who
whoever|[objective] whomever
whoever|[possessive] whosever
who|[possessive] whose
who|[objective] whom

Saída desejada:

why|[may express] surprise, reluctance, impatience, annoyance, indignation
whom|[used in] questions, subordination%[possessive] whose%[subjective] who
whoever|[objective] whomever%[possessive] whosever
who|[possessive] whose%[objective] whom
MichaelCodes
fonte
É permitida uma nova linha no início da saída?
MIllIbyte
adicionou comentários à pergunta original. E, @mIllIbyte, uma nova linha é irrelevante para mim. Mas, na minha ideia, não há linhas em branco nem verificação de erros. Suponho que todas as linhas tenham texto e, pelo menos, a primeira coluna e o delimitador.
18716 Michael Michael Código
A julgar pelos casos de teste, é salvo supor que todas as chaves estão agrupadas? Ou seja: ["A|some text", "B|other text", "A|yet some other text"]não é uma entrada desejada para teste, pois as palavras-chave para Anão são uma após a outra na lista.
Kevin Cruijssen
Eu assumi que todas as chaves estão agrupadas. Não estou preocupado com o caso em que não estejam, embora, em teoria, não sejam tratados como chaves únicas.
MichaelCodes

Respostas:

7

Retina , 17 bytes

  • 12 bytes salvos graças a @MartinEnder
  • 1 byte salvo graças a @ jimmy23013

Marcado em bytes codificados ISO 8859-1.

Usa em ;vez de |como o separador do campo de entrada.

(?<=(.+;).+)¶\1
%

Experimente online.

Trauma Digital
fonte
2
@LeakyNun Porque as lookarounds são atômicas. Na primeira vez em que a pesquisa é usada, ela captura todo o prefixo da linha e, posteriormente, o mecanismo de expressão regular não volta mais.
Martin Ender
5

V , 16 13 bytes

òí^¨á«©.*úsî±

Experimente online!

Você disse

Sinta-se livre para alterar o delimitador

Então eu escolhi |como delimitador. Se isso for inválido, informe-me e eu o alterarei.

Explicação:

ò                #Recursively:
 í               #Search for the following on any line:
  ^¨á«©          #1 or more alphabetic characters at the beginning of the line
       .*        #Followed by anything
         ús      #Mark everything after this to be removed:
           î±    #A new line, then the first match again (one or more alphabetic characters)
DJMcMayhem
fonte
1
Deixe você saber ???
Erik the Outgolfer
@ ΈρικΚωνσταντόπουλος Sim? Isso é um problema?
DJMcMayhem
Para este quebra-cabeça, fique à vontade para alterar o delimitador, por exemplo , use "%" como delimitador. não é #
Erik the Outgolfer
2
O "|" delimitador é bom.
18716 Michael Michael Code
@MichaelCodes Você poderia adicionar mais casos de teste para que possamos verificar se uma solução conta ou não?
DJMcMayhem
3

Perl -0n, 2 + 43 = 45 bytes

s/
.*\|/%/g,print for/(.*\|)((?:
\1|.)*
)/g

Demo:

$ perl -0ne 's/
> .*\|/%/g,print for/(.*\|)((?:
> \1|.)*
> )/g' <<EOF
> why|[may express] surprise, reluctance, impatience, annoyance, indignation
> whom|[used in] questions, subordination
> whom|[possessive] whose
> whom|[subjective] who
> whoever|[objective] whomever
> whoever|[possessive] whosever
> who|[possessive] whose
> who|[objective] whom
> EOF
why|[may express] surprise, reluctance, impatience, annoyance, indignation
whom|[used in] questions, subordination%[possessive] whose%[subjective] who
whoever|[objective] whomever%[possessive] whosever
who|[possessive] whose%[objective] whom
Anders Kaseorg
fonte
3

SQL (PostgreSQL), 43 72 bytes

COPY T FROM'T'(DELIMITER'|');SELECT a,string_agg(b,'%')FROM T GROUP BY A

Isso tira proveito da útil função agregada string_agg no PostgreSQL. A entrada é de uma tabela chamada Tcom 2 colunas Ae B. Para melhor atender à pergunta, incluí o comando para carregar dados de um arquivo na tabela. O arquivo também está T. Não contei a instrução de criação da tabela.
A saída será desordenada, mas se for um problema, poderá ser corrigida com umORDER BY A

O SQLFiddle não queria jogar para mim, mas é isso que recebo na minha instalação.

CREATE TABLE T (A VARCHAR(9),B VARCHAR(30));

COPY T FROM'T'(DELIMITER'|');SELECT a,string_agg(b,'%')FROM T GROUP BY A
a   string_agg
--- ----------------------------------------
c   rest of fourth line
b   rest of second line%rest of third line
a   rest of first line
d   rest of fifth line%rest of sixth line
MickyT
fonte
1
Para ser justo, sugiro incluir um comando COPY para ler também o conteúdo do formato de arquivo especificado na tabela, caso contrário você não estará resolvendo o mesmo problema que todos os outros.
Jules
@Jules Fair bastante, eu estava pensando neste padrão de consenso de E / S quando respondi. Relendo a pergunta, embora eu edite a resposta.
MickyT
2

C, 127 bytes

o[99],n[99],p=n;main(i){for(;gets(n);strncmp(o,n,i-p)?printf(*o?"\n%s":"%s",n),strcpy(o,n):printf(" /%s",i))i=1+strchr(n,'|');}

Funciona com o gcc. Delimitador alterado para /. Pega a entrada do stdin e grava a saída no stdout, então chame com o redirecionamento de entrada./a.out <filename

Ungolfed:

o[99],n[99] //declare int, to save two bytes for the bounds
,p=n; //p is an int, saves one byte as opposed to applying an (int) cast to n,
//or to declaring o and n as char arrays
main(i){for(;gets(n);strncmp(o,n,i-p //an (int)n cast would be needed;
// -(n-i) does not work either,
//because pointer arithmetics scales to (int*)
)?printf(*o?"\n%s":"%s" //to avoid a newline at the beginning of output
,n),strcpy(o,n):printf(" /%s",i))i=1+strchr(n,'|');}
mIllIbyte
fonte
1

Pitão - 15 bytes

Fazer algumas suposições sobre o problema mudará quando o OP esclarecer.

jm+Khhd-sdK.ghk

Experimente online aqui .

Maltysen
fonte
Isso não funciona se a "chave" for uma palavra, e não uma única letra. (OP esclarecido nos comentários)
DJMcMayhem
1

Python 3 - 146 bytes

Entrada é o nome do arquivo ou o caminho do arquivo, a saída é para stdout. Poderia ser muito menor se eu pudesse receber a entrada como texto bruto da linha de comando

Leva a entrada de stdin e as saídas para stdin. Instalação com separador "|". Para testar a primeira entrada de exemplo, use o separador" | "

from itertools import*
for c,b in groupby([x.split("|")for x in input().split("\n")],key=lambda x:x[0]):print(c,"|"," % ".join((a[1]for a in b)))
Keatinge
fonte
O desafio não exige explicitamente que a entrada seja lida em um arquivo, então acho que nossos métodos de E / S padrão se aplicam aqui. E como outras respostas também recebem a contribuição de STDIN, suponho que o OP esteja bem com isso.
Denker19 /
@DenkerAffe Tudo bem, eu vou editá-lo, ele será completamente inútil, porque eu não acho que você possa sequer fornecer informações multilinhas reais do stdin.
precisa saber é o seguinte
Mas você pode fazer o redirecionamento de entrada ao executar o script.
MIllIbyte
1

Java 7, 167 bytes

Provavelmente, pode ser jogado mais usando uma abordagem diferente.

import java.util.*;Map c(String[]a){Map m=new HashMap();for(String s:a){String[]x=s.split("=");Object l;m.put(x[0],(l=m.get(x[0]))!=null?l+"%"+x[1]:x[1]);}return m;}

NOTA: O método acima cria e retorna a HashMapcom os pares de valores-chave desejados. No entanto, ele não é impresso na saída exata, como na pergunta do OP, |como delimitador de saída entre as chaves e os novos valores. A julgar pela resposta SQL do MickeyT, onde ele retornou uma tabela de banco de dados, imaginei que isso fosse permitido; se não for necessário adicionar mais bytes para uma função de impressão.

Ungolfed & código de teste:

import java.util.*;

class Main{

    static Map c(String[] a){
        Map m = new HashMap();
        for(String s : a){
            String[] x = s.split("\\|");
            Object l;
            m.put(x[0], (l = m.get(x[0])) != null
                            ? l + "%" + x[1]
                            : x[1]);
        }
        return m;
    }

    public static void main(String[] a){
        Map m = c(new String[]{
            "why|[may express] surprise, reluctance, impatience, annoyance, indignation",
            "whom|[used in] questions, subordination",
            "whom|[possessive] whose",
            "whom|[subjective] who",
            "whoever|[objective] whomever",
            "whoever|[possessive] whosever",
            "who|[possessive] whose",
            "who|[objective] whom"
        });

        // Object instead of Map.EntrySet because the method returns a generic Map
        for (Object e : m.entrySet()){
            System.out.println(e.toString().replace("=", "|"));
        }
    }
}

Resultado:

whoever|[objective] whomever%[possessive] whosever
whom|[used in] questions, subordination%[possessive] whose%[subjective] who
why|[may express] surprise, reluctance, impatience, annoyance, indignation
who|[possessive] whose%[objective] whom
Kevin Cruijssen
fonte
1

PowerShell, 85 bytes

Strings são mescladas usando hashtable:

%{$h=@{}}{$k,$v=$_-split'\|';$h.$k=($h.$k,$v|?{$_})-join'%'}{$h.Keys|%{$_+'|'+$h.$_}}

Exemplo

Como o PowerShell não oferece suporte ao redirecionamento stdin via <, suponho que Get-Content .\Filename.txt |será usado como método de E / S padrão.

Get-Content .\Filename.txt | %{$h=@{}}{$k,$v=$_-split'\|';$h.$k=($h.$k,$v|?{$_})-join'%'}{$h.Keys|%{$_+'|'+$h.$_}}

Resultado

whoever|[objective] whomever%[possessive] whosever
why|[may express] surprise, reluctance, impatience, annoyance, indignation
whom|[used in] questions, subordination%[possessive] whose%[subjective] who
who|[possessive] whose%[objective] whom
beatcracker
fonte
1

APL, 42 caracteres

{⊃{∊⍺,{⍺'%'⍵}/⍵}⌸/↓[1]↑{(1,¯1↓'|'=⍵)⊂⍵}¨⍵}
lstefano
fonte
não é um byte na codificação APL.
Zacharý 31/07
0

Sed, 55 bytes

:a N;:b s/^\([^|]*\)|\([^\n]*\)\n\1|/\1|\2 %/;ta;P;D;tb

Execução de teste:

$ echo """why|[may express] surprise, reluctance, impatience, annoyance, indignation
> whom|[used in] questions, subordination
> whom|[possessive] whose
> whom|[subjective] who
> whoever|[objective] whomever
> whoever|[possessive] whosever
> who|[possessive] whose
> who|[objective] whom""" | sed ':a N;:b s/^\([^|]*\)|\([^\n]*\)\n\1|/\1|\2 %/;ta;P;D;tb'
why|[may express] surprise, reluctance, impatience, annoyance, indignation
whom|[used in] questions, subordination %[possessive] whose %[subjective] who
whoever|[objective] whomever %[possessive] whosever
who|[possessive] whose %[objective] whom
Aaron
fonte
0

q / kdb +, 46 bytes

Solução:

exec"%"sv v by k from flip`k`v!("s*";"|")0:`:f

Exemplo:

q)exec"%"sv v by k from flip`k`v!("s*";"|")0:`:f
who    | "[possessive] whose%[objective] whom"
whoever| "[objective] whomever%[possessive] whosever"
whom   | "[used in] questions, subordination%[possessive] whose%[subjective] who"
why    | "[may express] surprise, reluctance, impatience, annoyance, indignation"

Explicação:

`:f            // assumes the file is named 'f'
("s*";"|")0:   // read in file, assume it has two columns delimitered by pipe
flip `k`v      // convert into table with columns k (key) and v (value)
exec .. by k   // group on key
"%"sv v        // join values with "%"
rua
fonte