我正在循环遍历XMP响应:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<SearchRS>
<SearchStatus>SUCCESS</SearchStatus>
<Itinerary>
<Name>Joe</Name>
<Ticket>111.11</Ticket>
<Taxes>1.11</Taxes>
</Itinerary>
<Itinerary>
<Name>Bob</Name>
<Ticket>222.22</Ticket>
<Taxes>2.22</Taxes>
</Itinerary>
<Itinerary>
<Name>Joe</Name>
<Ticket>333.33</Ticket>
<Taxes>3.33</Taxes>
</Itinerary>
<Itinerary>
<Name>Bob</Name>
<Ticket>444.44</Ticket>
<Taxes>4.44</Taxes>
</Itinerary>
<Itinerary>
<Name>Joe</Name>
<Ticket>0.0</Ticket>
<Taxes>0.0</Taxes>
</Itinerary>
</SearchRS>
所以我创建了一个地图,用每个名字的平均票价+税收来收集这些信息。这是代码:
def xml = new XmlSlurper().parseText(xmlString)
def map = [:]
xml.'**'.findAll {it.name() == 'Name'}.unique().each { name ->
map[name] = xml.'**'.findAll {it.name() == 'Itinerary' && name == it.Name.text() }.collect { Double.parseDouble(it.Ticket.text()) + Double.parseDouble(it.Taxes.text())}.findAll {it}.with { sum() / size() }
}
这给我一个看起来像这样的结果:
[Joe:10.9101234, Bob:20.319999999999997]
我想把它看起来像这样,但我不知道在哪里放置圆(2)方法。拨打:
[Joe:10.91, Bob:20.31]
任何帮助表示赞赏!
答案 0 :(得分:0)
考虑简化您的示例。您可以使用与常见的fold-left操作类似的Groovy inject {}
操作来代替嵌套迭代,而是迭代列表并将结果累积到不同的形式。看一下这个例子:
import groovy.util.slurpersupport.NodeChild
import java.math.RoundingMode
def input = '''<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<SearchRS>
<SearchStatus>SUCCESS</SearchStatus>
<Itinerary>
<Name>Joe</Name>
<Ticket>111.11</Ticket>
<Taxes>1.11</Taxes>
</Itinerary>
<Itinerary>
<Name>Bob</Name>
<Ticket>222.22</Ticket>
<Taxes>2.22</Taxes>
</Itinerary>
<Itinerary>
<Name>Joe</Name>
<Ticket>333.33</Ticket>
<Taxes>3.33</Taxes>
</Itinerary>
<Itinerary>
<Name>Bob</Name>
<Ticket>444.44</Ticket>
<Taxes>4.44</Taxes>
</Itinerary>
<Itinerary>
<Name>Joe</Name>
<Ticket>0.0</Ticket>
<Taxes>0.0</Taxes>
</Itinerary>
</SearchRS>'''
def xml = new XmlSlurper().parseText(input)
def result = xml.'*'.findAll { node ->
node.name() == 'Itinerary'
}.inject([:]) { Map<String, List<BigDecimal>> map, NodeChild node ->
// sum ticket + tax
def value = node.Ticket.text().toBigDecimal() + node.Taxes.text().toBigDecimal()
// collect all values as Name => List of prices
map.merge(node.Name.text(), [value], { a, b -> a + b })
return map
}.collectEntries { String k, List<BigDecimal> v ->
// calculate average price per name and round the final result
[(k): (v.sum() / v.size()).setScale(2, RoundingMode.HALF_UP)]
}
println result
首先,我们过滤所有子节点,使其仅适用于Itinerary
个节点。接下来我们调用inject([:])
,它以空映射开始,然后我们开始迭代所有节点。迭代的每一步都是:
Ticket
和Taxes
值来计算值,并将其存储为BigDecimal
Map.merge(key, value, remappingFunction)
,因此如果给定的密钥不存在,则将密钥与存储在列表中的值放在一起。如果存在,我们将值添加到现有列表中(通过合并列表中打包的新值)map
变量最后,我们调用重新映射初始地图的collectEntries
方法 - 将Map<String, List<BigDecimal>>
转换为Map<String, BigDecimal>
,其中存储为BigDecimal
的值是根据存储在列表中的值。当我们计算最终值时,我们可以调用BigDecimal.setScale(scale, mode)
来围绕它。
当你运行这个例子时,你会得到这样的东西:
[Joe:149.63, Bob:336.66]
Java 8引入了这个Map.merge(key, value, remappingFunction)
有用的方法。但是,它可以用更为迫切的方式表达,如:
def key = node.Name.text()
if (!map.containsKey(key)) {
map.put(key, [])
}
map.put(key, map.get(key) << value)
如果您使用的Java早于Java 8,请使用这4行而不是map.merge(...)
。
希望它有所帮助。
答案 1 :(得分:0)
作为Szymon的答案的略微简化,我们可以直接使用toBigDecimal
(NodeChild实例)的node
方法,我们可以用withDefault
替换合并打电话给初始地图:
import groovy.util.slurpersupport.NodeChild
import java.math.RoundingMode
def input = '''<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<SearchRS>
<SearchStatus>SUCCESS</SearchStatus>
<Itinerary>
<Name>Joe</Name>
<Ticket>111.11</Ticket>
<Taxes>1.11</Taxes>
</Itinerary>
<Itinerary>
<Name>Bob</Name>
<Ticket>222.22</Ticket>
<Taxes>2.22</Taxes>
</Itinerary>
<Itinerary>
<Name>Joe</Name>
<Ticket>333.33</Ticket>
<Taxes>3.33</Taxes>
</Itinerary>
<Itinerary>
<Name>Bob</Name>
<Ticket>444.44</Ticket>
<Taxes>4.44</Taxes>
</Itinerary>
<Itinerary>
<Name>Joe</Name>
<Ticket>0.0</Ticket>
<Taxes>0.0</Taxes>
</Itinerary>
</SearchRS>'''
def xml = new XmlSlurper().parseText(input)
def result = xml.Itinerary.inject([:].withDefault{[]}) { m, n ->
m[n.Name] << n.Ticket.toBigDecimal() + n.Taxes.toBigDecimal()
m
}.collectEntries { name, v ->
[(name): (v.sum() / v.size()).setScale(2, RoundingMode.HALF_UP)]
}
println result
或者说名字首先是groupBy更简单:
result = xml.Itinerary.groupBy { it.Name }.collectEntries { k, v ->
def c = v.collect { it.Ticket.toBigDecimal() + it.Taxes.toBigDecimal() }
[k, (c.sum() / c.size).setScale(2, RoundingMode.HALF_UP)]
}
打印:
~> groovy test.groovy
[Joe:149.63, Bob:336.66]
假设我已正确理解问题。