Groovy:map减少地图列表

时间:2015-08-28 14:00:24

标签: groovy mapreduce functional-programming

假设我有Groovy中的地图列表:

def listOfMaps = [
[k: 1, n: 'Name1', d1: 'a', d2: 'b'],
[k: 2, n: 'Name2', d1: 'c', d2: 'd'],
[k: 1, n: 'Name3', d1: 'e', d2: 'f'],
[k: 4, n: 'Name4', d1: 'g', d2: 'h']]

我需要查找是否存在(或不存在)项目,其中k相等,但n不存在。例如。在这种情况下,我们有两个地图记录,其中“k”= 1,“n”是“Name1”和“Name3”。我怎样才能找到这样的数据? 我想我应该按“k”分组并在“n”中计算不同的值,如果“n”中有超过1个唯一值用于某些“k” - 我们找到了这样的数据。 我完全陷入困境,所以任何帮助都会受到赞赏。 感谢

5 个答案:

答案 0 :(得分:3)

修改

现在我已经找到了你的意思,这里是代码:

listOfMaps.groupBy { 
   it.k }.
values().
findAll { l -> 
   l.size() > 1 && (l.size() == l.unique { e -> e.n }.size()) 
}

在开始时,列表按k元素分组,然后在我们搜索大小大于1且大小等于唯一n元素数量的列表的值中。它工作正常。

OLD ANSWERS

您可以尝试findAllunique

的组合
def listOfMaps = [
    [k: 1, n: 'Name1', d1: 'a', d2: 'b'],
    [k: 2, n: 'Name2', d1: 'c', d2: 'd'],
    [k: 1, n: 'Name3', d1: 'e', d2: 'f'],
    [k: 4, n: 'Name4', d1: 'g', d2: 'h'],
]

listOfMaps.findAll { it.k == 1 }.unique { it.n }

groupBy

listOfMaps.groupBy { it.k }[1].unique { it.n }

在groovy中有很多方法可以做到这一点;)

答案 1 :(得分:2)

listOfMaps.groupBy { [it.k, it.n] }.keySet().countBy { it[0] }.any { it.value > 1 }

您需要的只是k&{39}和n的组合进行比较。您可以按groupBy的方式进行操作。我更喜欢按列表分组,以便我getAt(0)检查是否存在重复项。由于keySet()Set,因此没有两个项目(列表)将是相同的。然后我们只需检查第一项(k)是否唯一。

答案 2 :(得分:2)

如果您对缩小版感兴趣,可以将k的地图构建为一组n s。

def r = listOfMaps.inject([:].withDefault{[].toSet()}) { m, it -> 
    m.get(it.k).add(it.n); m }
println r.findAll{ it.value.size()>1 }
// => [1:[Name3, Name1]]

答案 3 :(得分:0)

我无法找到比较 n 的方法,但请试一试:

// Finds a match
assert [[k: 1, n: 'Name1', d1: 'a', d2: 'b'],
[k: 2, n: 'Name2', d1: 'c', d2: 'd'],
[k: 1, n: 'Name3', d1: 'e', d2: 'f'],
[k: 4, n: 'Name4', d1: 'g', d2: 'h']]
    .groupBy { it.k }    
    .collectEntries {k, v ->
        ["$k": v.unique()]
    }    
    .findAll { it.value.size() > 1 } != [:]

// Does not find a match
assert [[k: 2, n: 'Name2', d1: 'c', d2: 'd'],
[k: 1, n: 'Name3', d1: 'e', d2: 'f'],
[k: 4, n: 'Name4', d1: 'g', d2: 'h']]
    .groupBy { it.k }    
    .collectEntries {k, v ->
        ["$k": v.unique()]
    }    
    .findAll { it.value.size() > 1 } == [:]

修改

我把它拿回来。 n可以与unique()进行比较:

[[k: 1, n: 'Name1', d1: 'a', d2: 'b'],
[k: 2, n: 'Name2', d1: 'c', d2: 'd'],
[k: 1, n: 'Name3', d1: 'e', d2: 'f'],
[k: 4, n: 'Name4', d1: 'g', d2: 'h']]
    .groupBy { it.k }    
    .collectEntries {k, v ->
        ["$k": v.unique { a, b -> (a.k == b.k && a.n == b.n) ? 0 : 1 }]
    }    
    .findAll { it.value.size() > 1 } != [:]

坐在马桶上的东西真是太神奇了。

答案 4 :(得分:0)

是的,Groovy确实非常时髦,您可以使用不同的方法来实现相同的目标。

我会添加groupBy + unique + grep方法。它可能更容易理解。

基本上你需要的是通过collect / unique来减少维度,然后按照你收集的大小grep。

def find = { 
    it.groupBy { it.k } // Fold one dimension
        .grep { it.value.size() > 1 } // filter results by that
        .grep { it.value*.n.unique().size() > 1 } // fold and repeat
        .collectEntries() // back to map
}

def listOfMaps = [
    [k: 1, n: 'Name1', d1: 'a', d2: 'b'],
    [k: 2, n: 'Name2', d1: 'c', d2: 'd'],
    [k: 1, n: 'Name3', d1: 'e', d2: 'f'],
    [k: 4, n: 'Name4', d1: 'g', d2: 'h']]


assert find(listOfMaps) == [1: [[k: 1, n: 'Name1', d1: 'a', d2: 'b'], [k: 1, n: 'Name3', d1: 'e', d2: 'f']]]

listOfMaps[2].n = "Name1" // Make them equals.
assert find(listOfMaps) == [:]