簡體   English   中英

列表的Groovy地圖到地圖列表

[英]Groovy Map of Lists into List of Maps

所以基本上我有這樣的事情:

[a: ["c","d"], b: ["e","f"]]

每個列表中的項目數量是任意的。 如果只有一個項目,則列表不再是列表,而是一個字符串。

我想把它變成:

[ [a:"c", b:"e"], [a:"d",b:"f"] ]

我真的不在乎解決方案是否使用Groovy方法。 謝謝你的幫助!

這是另一種方法,我認為不那么模糊,同時仍然相當簡潔:

def ml = [a: ["c","d"], b: ["e","f"]]

// Create an empty list that creates empty maps as needed
def lm = [].withDefault{ [:] }

ml.each{ k, values ->
    [values].flatten().eachWithIndex { value, index ->
        lm[index][k] = value
    }
}

assert lm == [[a:"c", b:"e"], [a:"d", b:"f"]]

如果您不想或不能使用withDefault (因為您不希望列表自動增長),那么這也適用:

def ml = [a: ["c","d"], b: ["e","f"]]

def lm = []

ml.each{ k, values ->
    [values].flatten().eachWithIndex { value, index ->
        lm[index] = lm[index] ?: [:]
        lm[index][k] = value
    }
}

assert lm == [[a:"c", b:"e"], [a:"d", b:"f"]]

編輯 :添加代碼以處理列表中未包含的字符串。

注意,給定的技巧( [values].flatten().eachWithIndex{...} )不一定非常有效。 如果速度是必不可少的,那么使用它會稍微快一些,但會犧牲可讀性:

(values instanceof List ? values : [values]).eachWithIndex{...}

單行,假設x = [a: ["c","d"], b: ["e","f"]]或x = [a: "b", c: "d"]

[x*.key, x*.value].transpose()*.combinations().transpose()*.flatten()*.toSpreadMap()

這是如何工作的:

首先,拆分鍵和值:

[x*.key, x*.value] = [[a, b], [[c, d], [e, f]]]

轉置它們以配對鍵和值:

[[a, b], [[c, d], [e, f]]].transpose() = [[a, [c, d]], [b, [e, f]]]

使用combinations將密鑰與其值配對(此處使用的擴展運算符將其應用於每個列表元素)。 請注意,組合將正確處理[a:b][a:[b,c]]

[[a, [c, d]], [b, [e, f]]]*.combinations() = [[[a, c], [a, d]], [[b, e], [b, f]]]

轉置列表以便我們最終得到abab而不是aabb(雖然有點嵌套):

[[[a, c], [a, d]], [[b, e], [b, f]]].transpose() = [[[a, c], [b, e]], [[a, d], [b, f]]]

展平嵌套列表(再次使用spread來展平嵌套列表,但不是整個列表):

[[[a, c], [b, e]], [[a, d], [b, f]]]*.flatten() = [[a, c, b, e], [a, d, b, f]]

傳播到toSpreadMap將此列表轉換為地圖列表。

[[a, c, b, e], [a, d, b, f]]*.toSpreadMap() = [*:[b:e, a:c], *:[b:f, a:d]]

定義一些功能:

// call a 2-element list a "pair"

// convert a map entry (where entry.value can be 
// a single string or a list of strings) into a list of pairs
def pairs(entry) {
  if (entry.value instanceof String)
    return [[entry.key, entry.value]]
  entry.value.collect { [entry.key, it]}
}

// convert list of pairs to a map
def toMap(pairs) {
  pairs.inject([:]){ m,i -> m[i[0]] = i[1]; m }
}

// kind of like transpose but doesn't stop with shortest list. 
// (would like to find a less ugly way of doing this)
def mytranspose(lists) {
  def retval = []
  def mx = lists.inject(0){x, i -> i.size() > x ? i.size() : x}
  for (int i = 0; i < mx; i++) {
    def row = []
    lists.each { lst ->
      if (lst.size() > i) row << lst[i]
    }
    retval << row
  }
  retval
}

然后把它放在一起測試它:

groovy:000> m = [a: ["c","d"], b: ["e","f"]]
groovy:000> mytranspose(m.entrySet().collect{pairs(it)}).collect{toMap(it)}
===> [{a=c, b=e}, {a=d, b=f}]

字符串的映射條目有效,映射條目列表的長度可以不同:

groovy:000> m['g'] = 'h'
===> h
groovy:000> m['x'] = ['s', 't', 'u', 'v']
===> [s, t, u, v]
groovy:000> m
===> {a=[c, d], b=[e, f], g=h, x=[s, t, u, v]}
groovy:000> mytranspose(m.entrySet().collect{pairs(it)}).collect{toMap(it)}
===> [{a=c, b=e, g=h, x=s}, {a=d, b=f, x=t}, {x=u}, {x=v}]

這就是我最終做的事情。 如果有人有更好的解決方案,請告訴我,我會接受它作為答案。

  Map xyz = [a: ["c","d"], b: ["e","f"]]

  List result = []

    Closure updateMap = { list, index, key, value ->
        if ( !(list[index] instanceof Map) ) {
            list[index] = [:]
        }
        list[index]."$key" = value
    }

    xyz.each { k, v ->
        if (v instanceof ArrayList) {
            v.eachWithIndex { val, idx ->
                updateMap(result, idx, k, val)
            }
        }
        else {
            updateMap(result, 0, k, v)
        }
    }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM