1

Suppose having XML:

<?xml version="1.0" encoding="UTF-8"?>
<data>
    <level0 id="2" t="1">
        <level1 id="lev1id21" att1="2015-05-12" val="121" status="0" year="2015" month="05" />
        <level1 id="lev1id22" att1="2015-06-13" val="132" status="0" year="2015" month="06" />
        <level1 id="lev1id23" att1="2015-07-11" val="113" status="0" year="2015" month="08" />
        <level1 id="lev1id23" att1="2015-07-11" val="114" status="0" year="2015" month="07" />
    </level0>
</data>

I have to find level1 node by conditions (assuming we could have many level0 siblings):

  1. For each level0 find all 'level1' nodes which have maximal att1 value (interpreted as Date in yyyy-mm-dd)
  2. Among those level1 nodes find one that has maximal value in year and month attributes, interpreted as ints.

For given example, I expect node with val="113" value to be found. Since I'm not an expert in GPath, please help to find a correct and Groovish solution. Thanks.

lospejos
  • 1,976
  • 3
  • 19
  • 35
  • Your expected result does not match your criteria. You want to sort the nodes by `att1`, then by `year`, then by `month`? In that case I would expect a result of `113` given your data. – thecodesmith_ Aug 12 '16 at 02:52
  • @thecodesmith_ Thanks, I updated expected result, you're right, I expect to get 113 – lospejos Aug 12 '16 at 06:25

2 Answers2

2

The expected behavior is a bit unclear, see my comment on the post. However, I'm working off the assumption you want to sort the data by att1, then by year, then by month, and find the max value.

To do it in a Groovy way, I'd extract some helper methods so you can see what is going on:

def date = { Date.parse('yyyy-MM-dd', it.@att1.toString()) }
def year = { it.@year.toString() }
def month = { it.@month.toString() }

Then you can sort the nodes using the "space-ship" operator <=> to do comparison, and using the "elvis" operator ?: to do the next level comparison if the first returns 0 (which happens when the comparison is equal):

def nodes = new XmlSlurper().parseText(xml).level0.level1

def max = nodes.sort { a, b ->
    date(a) <=> date(b) ?:
            year(a) <=> year(b) ?:
                    month(a) <=> month(b)
} .collect { it.@val } .last()

println max  
// Prints "113", given your data above
thecodesmith_
  • 1,277
  • 8
  • 18
1

For now I've found this solution, I'd like to know if there is a more Groovish way to do this.

def xml='''<?xml version="1.0" encoding="UTF-8"?>
<data>
    <level0 id="2" t="1">
        <level1 id="lev1id21" att1="2015-05-12" val="121" status="0" year="2015" month="05" />
        <level1 id="lev1id22" att1="2015-06-13" val="132" status="0" year="2015" month="06" />
        <level1 id="lev1id23" att1="2015-07-11" val="113" status="0" year="2015" month="08" />
        <level1 id="lev1id23" att1="2015-07-11" val="114" status="0" year="2015" month="07" />
    </level0>
</data>'''

def nodes = new XmlSlurper().parseText(xml).level0.level1.findAll {level1 ->
    level1.max {lev1->
        Date.parse('yyyy-MM-dd',lev1.@att1.toString())
    }
}
.each {level1 ->
    level1.max { lev1 ->
        lev1.@year.toString() as int
    }
}.max {level1 ->
    level1.@month.toString() as int
}.collect()

println "${nodes.count {it}}"

nodes.each { n ->
    println "val = ${n.@val}"
}
M.Void
  • 2,764
  • 2
  • 29
  • 44
lospejos
  • 1,976
  • 3
  • 19
  • 35