Combine or group lines by same words

Viewed 63

Combine or group lines by same words using "and", "or":

  • If line has single class then combine using "or" such as

    .class1

    .class2

    .class3

    to

    (.class1 or .class2 or .class3)

  • If line already has two or more class and any classes are matching with other lines then it combined following ways:

    .class4 .class5

    .class4 .class6

    .class9 .class10 .class11

    .class9 .class10 .class12

    to

    .class4 and (.class5 or .class6)

    .class9 and .class10 and (.class11 or .class12)

Here is an example of text file

file.txt

.class1
.class2
.class3
.class4
.class4 .class5
.class4 .class6
.class7 .class8
.class9
.class9 .class10 
.class9 .class10 .class11
.class9 .class10 .class12

expected

(.class1 or .class2 or .class3 or .class4 or .class9)
.class4 and (.class5 or .class6)
.class7 and .class8
.class9 and .class10
.class9 and .class10 and (.class11 or .class12)

Here is what I tried:

awk '/ /{if (x)print x;x="";}{x=(!x)?$0:x" or "$0;}END{print x;}' file.txt > file1.txt

got following result:

.class1 or .class2 or .class3 or .class4
.class4 .class5
.class4 .class6
.class7 .class8 or .class9
.class9 .class10 
.class9 .class10 .class11
.class9 .class10 .class1

then

awk 'BEGIN{FS=OFS=" "} {c=$1 FS $3; if (c in a) a[c]=a[c] FS $2; else a[c]=$2} END{for (k in a) print k " and", a[k]}' file1.txt > file2.txt

gives

.class4  and .class5 .class6
.class9  and .class10
.class7 or and .class8
.class1 .class2 and or
.class9 .class11 and .class10
.class9 .class12 and .class10
1 Answers

This might work for you (GNU sed):

sed -E 's/^((.* )*)(\S+)$/\1(\3)/
        :b;N;s/^(.*)\((.*)\)\n\1(\S+)$/\1(\2 or \3)/;$!tb
        s/\s+\n/\n/
        h;s/(\(|\n).*/\1/;s/ / and /g;x;s/^[^\n(]*[\n(]//;H;x;s/\n//
        s/\((\S+)\)/\1/;P;D' file

Surround the last class by parens.

Append another line and if that lines size matches the previous, reduce the two lines to the size of the first with the last field of the second line included within the first lines parens separated by or.

Remove any spaces at the end of the line.

If the size of the append line is not the same, replace the spaces to the left of the parens (or if there are no parens to the end of the line) by and.

If parens enclose a singe word, remove them.

Print then delete the first line and repeat.

N.B. By size understand number of words as well as duplicate keys where non-duplicates keys (if present) indicate a change in size.

Related