Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I have some data stored in JSON-like format.

{
    {
        value1: 14,
        value2: 12,
        value3: 1
    },
    {
        value1: 4,
        value3: -1
    }
}

How can I get subsection with maximum 'value3' value using awk (which is most preferable and related to that kind of problems, I think), sed or grep?

So, this output is expecting:

{
    value1: 14,
    value2: 12,
    value3: 1
}
share|improve this question
    
What is the number of 'levels' of curly braces? Is it reasonably short, or can it be arbitrarily long? –  Marcin Nov 29 '11 at 15:03
    
Number of 'levels' is constant and equal 2 like in example. –  ДМИТРИЙ МАЛИКОВ Nov 29 '11 at 15:04
add comment

1 Answer

up vote 6 down vote accepted

Supposing the formatting is always as in example – one value or section delimiter per line:

awk '/\{/{s="";i=1}i{s=s"\n"$0}$1=="value3:"{v=$2}/\}/{if(V==""||V<v){V=v;S=s}i=0}END{print S}' json-like.file

An RS-based alternative, in case not getting the section delimiters is acceptable:

awk -vRS='}' '{sub(/.*\{/,"")}match($0,/value3: (\S+)/,m)&&(v==""||v<m[1]){v=m[1];s=$0}END{print s}' json-like.file

An RT-based alternative:

awk -vRS='\\{[^{}]+\\}' 'match(RT,/value3: (\S+)/,m)&&(v==""||v<m[1]){v=m[1];s=RT}END{print s}' json-like.file

Explanations as requested in comment.

awk '
/\{/{s="";i=1}   # start of section? reset accumulated section data; set inside section flag
i{s=s"\n"$0}   # inside section? append current record to the accumulated data
$1=="value3:"{v=$2}   # value3 entry? store its value
/\}/{if(V==""||V<v){V=v;S=s}i=0}   # end of section? if no previous max or previous max value less than current value then set maxvalue to value and max section to section; reset inside section flag
END{print S}   # output max section
' json-like.file

awk -vRS='}' '   # record separator is the end of section delimiter
{sub(/.*\{/,"")}   # remove start of section delimiter and anything before it
match($0,/value3: (\S+)/,m)&&(v==""||v<m[1]){v=m[1];s=$0}   # current record contains value3 entry and no previous max or previous max value is less than its value? set max value to value and section to current record
END{print s}   # output section
' json-like.file

awk -vRS='\\{[^{}]+\\}' '   # record separator is an entire section
match(RT,/value3: (\S+)/,m)&&(v==""||v<m[1]){v=m[1];s=RT}   # current record terminator contains value3 entry and no previous max or previous max value is less than its value? set max value to value and section to current record terminator
END{print s}   # output section
' json-like.file
share|improve this answer
    
Nicely done! If you get a chance can you add some explanation as to what awk is doing for learning purposes. –  jaypal Nov 29 '11 at 16:06
    
Thanks @manatwork. Impressive!! :) –  jaypal Nov 29 '11 at 19:51
1  
I found a bug. What if all the values will be negative? You need to initialize V as -INFINITY or something. –  ДМИТРИЙ МАЛИКОВ Nov 30 '11 at 9:03
    
Indeed. But that as no actual -infinity is supported in awk, that would be just a special value needing special treatment. V and v are already initialized with a special value: the empty string. Added the special treatment part to all of them. –  manatwork Nov 30 '11 at 9:16
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.