# Oneliner to merge lines with same first field

This is my first codegolf question, so I apologize in advance if it's not appropriate, and I welcome any feedback.

I have a file with this format:

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


The actual contents vary, as does the delimiter. The contents are just text. The delimiter only appears once per line. For this puzzle, feel free to change the delimiter, e.g. use "%" as the delimiter.

Desired output:

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


I already have both ruby and awk scripts to merge this, but I suspect it's possible to have a short oneliner. i.e. a one-liner that can be used along with pipes and other commands on the command line. I can't figure it out, and my own script is to long to just compress on the command line.

Shortest characters preferred. Input is not necessarily sorted, but we are only interested in merging consecutive lines with matching first fields. There are unlimited lines with matching first fields. Field 1 could be anything, e.g. names of fruits, proper names, etc.

(I run on MacOS, so I am personally most interested in implementations that run on the mac).

Here is a second example/test. Notice "|" is the delimiter. The space before the "|" is irrelevant, and if resent should be considered part of the key. I am using "%" as a delimited in the output, but again, feel free to change the delimiter (but don't used square brackets).

Input:

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


Desired output:

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

• Is a newline at the beginning of the output allowed? – mIllIbyte Jun 1 '16 at 19:52
• added comments into original question. And, @mIllIbyte, a newline is irrelevant to me. But in my idea, there are no blank lines, and no error checking. I assume all lines have text, and at least the first column and the delimiter. – MichaelCodes Jun 1 '16 at 20:13
• Judging by the test cases, is it save to assume that all keys are grouped? I.e.: ["A|some text", "B|other text", "A|yet some other text"] isn't a desired input to test, since the keywords for A aren't one after another in the list. – Kevin Cruijssen Jun 2 '16 at 11:59
• I assumed all keys are grouped. I'm not concerned with the case where they are not, though in theory, it not they would be treated like unique keys. – MichaelCodes Jun 2 '16 at 14:30

# Retina, 17 bytes

• 12 bytes saved thanks to @MartinEnder
• 1 byte saved thanks to @jimmy23013

Scored in ISO 8859-1 encoded bytes.

Uses ; instead of | as the input field separator.

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


Try it online.

• retina.tryitonline.net/… – Leaky Nun Jun 1 '16 at 23:34
• @LeakyNun Because lookarounds are atomic. The first time the lookaround is used it captures the entire prefix of the line, and afterwards the regex engine won't backtrack into it any more. – Martin Ender Jun 2 '16 at 6:52

# V, 16 13 bytes

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


Try it online!

You said

Feel free to change the delimiter

So I picked | as the delimiter. If this is invalid, let me know and I'll change it.

Explanation:

ò                #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)

• Let you know??? – Erik the Outgolfer Jun 1 '16 at 18:51
• @ΈρικΚωνσταντόπουλος Yes? Is that a problem? – James Jun 1 '16 at 18:55
• For this puzzle, feel free to change the delimiter, e.g. use "%" as the delimiter. not i.e. – Erik the Outgolfer Jun 1 '16 at 18:57
• The "|" delimiter is fine. – MichaelCodes Jun 1 '16 at 19:22
• @MichaelCodes Could you add some more test cases so we can verify if a solution counts or not? – James Jun 1 '16 at 19:32

# 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  # SQL (PostgreSQL), 43 72 bytes COPY T FROM'T'(DELIMITER'|');SELECT a,string_agg(b,'%')FROM T GROUP BY A  This takes advantage of the handy string_agg aggregate function in PostgreSQL. Input is from a table called T with 2 columns A and B. To comply with the question better I have included to command to load data from a file into the table. The file is T as well. I haven't counted the table create statement. The output will be unordered, but if that is a problem it can be fixed with an ORDER BY A SQLFiddle didn't want to play for me, but this is what I get in my setup. 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  • To be fair, I'd suggest including a COPY command to read the contents of the file format specified into the table, too, otherwise you're not solving the same problem as everyone else. – Jules Jun 2 '16 at 6:54 • @Jules Fair enough, I was thinking of this default i/o concensus when I answered. Rereading the question though I'll edit the answer. – MickyT Jun 2 '16 at 19:29 # 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,'|');}  Works with gcc. Changed delimiter to /. Takes input from stdin and writes output to stdout, so call with input redirection ./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,'|');}  # Pyth - 15 bytes Making a few assumptions about the problem, will change when OP clarifies. jm+Khhd-sdK.ghk  • This doesn't work if the "key" is a word, rather than a single letter. (OP clarified in the comments) – James Jun 1 '16 at 18:47 # Python 3 - 146 Bytes Input is the filename or file path of the file, output is to stdout. Could be a lot shorter if I could take input as raw text from command line Takes input from stdin and outputs to stdin. Setup with separator "|". To test the first example input use the separator " | " 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)))  • The challenge doesn't explicitly require the input to be read from a file, so I guess our Default I/O methods apply here. And since other answers also take the input from STDIN, I suppose the OP is fine with it. – Denker Jun 2 '16 at 10:10 • @DenkerAffe Alright i'll edit it, it will just be completely useless because I don't think you can even give actual multiline input from stdin. – Keatinge Jun 2 '16 at 10:11 • But you can do input redirection when you run the script. – mIllIbyte Jun 2 '16 at 10:13 # Java 7, 167 bytes It can probably be golfed more by using a different approach.. 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;}  NOTE: The method above creates and returns a HashMap with the desired key-value pairs. However, it doesn't print it in the exact output as in OP's question with | as output-delimiter between the keys and new values. Judging by MickeyT's SQL answer where he returned a database table I figured this is allowed; if not more bytes should be added for a print function. Ungolfed & test code: 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("=", "|")); } } }  Output: 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  ### PowerShell, 85 bytes Strings are merged using hashtable: %{$h=@{}}{$k,$v=$_-split'\|';$h.$k=($h.$k,$v|?{$_})-join'%'}{$h.Keys|%{$_+'|'+$h.$_}}  ### Example Since PowerShell doesn't support stdin redirection via <, I'm assuming that Get-Content .\Filename.txt | will be used as default I/O method. Get-Content .\Filename.txt | %{$h=@{}}{$k,$v=$_-split'\|';$h.$k=($h.$k,$v|?{$_})-join'%'}{$h.Keys|%{$_+'|'+$h.$_}}  ### Output 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  ## APL, 42 chars {⊃{∊⍺,{⍺'%'⍵}/⍵}⌸/↓[1]↑{(1,¯1↓'|'=⍵)⊂⍵}¨⍵}  • ⌸ isn't one byte in the APL encoding. – Zacharý Jul 31 '17 at 22:40 # Sed, 55 bytes :a N;:b s/^$$[^|]*$$|$$[^\n]*$$\n\1|/\1|\2 %/;ta;P;D;tb  Test run : $ 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


# q/kdb+, 46 bytes

Solution:

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


Example:

q)exec"%"sv v by k from flipkv!("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"


Explanation:

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