2008-08-20 7 views
93

Je voudrais un peu sorthand pour cela:raccourci pour créer une carte à partir d'une liste dans groovy?

Map rowToMap(row) { 
    def rowMap = [:]; 
    row.columns.each{ rowMap[it.name] = it.val } 
    return rowMap; 
} 

compte tenu de la façon dont les choses GDK est, je pense pouvoir faire quelque chose comme:

Map rowToMap(row) { 
    row.columns.collectMap{ [it.name,it.val] } 
} 

mais je ne vu quelque chose dans les docs ... ai-je raté quelque chose? ou suis-je juste trop paresseux?

+2

commentaire de Amir est maintenant bonne réponse: http://stackoverflow.com/a/4484958/27561 –

Répondre

101

J'ai récemment rencontré le besoin de faire exactement cela: convertir une liste en une carte. Cette question a été publiée avant la publication de la version 1.7.9 de Groovy, donc la méthode collectEntries n'existait pas encore. Il fonctionne exactement comme la méthode collectMapthat was proposed:

Map rowToMap(row) { 
    row.columns.collectEntries{[it.name, it.val]} 
} 

Si pour une raison quelconque, vous êtes coincé avec une version plus ancienne Groovy, la méthode inject peut également être utilisé (comme proposé here). Ceci est une version légèrement modifiée qui prend une seule expression dans la fermeture (juste pour le plaisir de l'épargne de caractère!):

Map rowToMap(row) { 
    row.columns.inject([:]) {map, col -> map << [(col.name): col.val]} 
} 

L'opérateur + peut également être utilisé à la place du <<.

1

Je ne trouve rien construit ... mais en utilisant le ExpandoMetaClass je peux le faire:

ArrayList.metaClass.collectMap = {Closure callback-> 
    def map = [:] 
    delegate.each { 
     def r = callback.call(it) 
     map[r[0]] = r[1] 
    } 
    return map 
} 

cela ajoute la méthode collectMap à tous ArrayLists ... Je ne sais pas pourquoi l'ajouter Liste ou Collection ne fonctionne pas .. Je suppose que ce pour une autre question ... mais maintenant je peux le faire ...

assert ["foo":"oof", "42":"24", "bar":"rab"] == 
      ["foo", "42", "bar"].collectMap { return [it, it.reverse()] } 

de la liste à la carte calculée avec une fermeture ... exactement ce que je à la recherche de.

Edit: la raison pour laquelle je ne pouvais pas ajouter la méthode à la liste des interfaces et collection a été parce que je ne le fais pas:

List.metaClass.enableGlobally() 

après appel de méthode, vous pouvez ajouter des méthodes aux interfaces .. qui dans ce cas signifie que ma méthode collectMap fonctionnera sur les gammes comme ceci:

(0..2).collectMap{[it, it*2]} 

qui donne la carte: [0: 0, 1: 2, 2: 4]

5

ok ... Je l'ai joué avec cela un peu plus et je pense que c'est une méthode assez cool ...

def collectMap = {Closure callback-> 
    def map = [:] 
    delegate.each { 
     def r = callback.call(it) 
     map[r[0]] = r[1] 
    } 
    return map 
} 
ExpandoMetaClass.enableGlobally() 
Collection.metaClass.collectMap = collectMap 
Map.metaClass.collectMap = collectMap 

maintenant une sous-classe de carte ou Collection cette méthode ...

ici je l'utilise pour inverser la clé/valeur dans une carte

[1:2, 3:4].collectMap{[it.value, it.key]} == [2:1, 4:3] 

et ici je l'utilise pour créer une carte à partir d'une liste

[1,2].collectMap{[it,it]} == [1:1, 2:2] 

maintenant je viens de pop ceci dans une classe qui est appelée comme mon application commence et cette méthode est disponible dans tout mon code.

EDIT:

ajouter la méthode à tous les tableaux ...

Object[].metaClass.collectMap = collectMap 
0

Qu'en est-il quelque chose comme ça?

// setup 
class Pair { 
    String k; 
    String v; 
    public Pair(def k, def v) { this.k = k ; this.v = v; } 
} 
def list = [ new Pair('a', 'b'), new Pair('c', 'd') ] 

// the idea 
def map = [:] 
list.each{ it -> map.putAt(it.k, it.v) } 

// verify 
println map['c'] 
+0

C'est fondamentalement le même que dans ma question ... Je viens d'avoir map [it.k] = it.v au lieu de .putAt Je cherchais pour un seul paquebot. – danb

28

Vérifiez "injecter". Les vrais fonctionnels de programmation les appellent "fold".

columns.inject([:]) { memo, entry -> 
    memo[entry.name] = entry.val 
    return memo 
} 

Et, pendant que vous y êtes, vous voulez probablement définir des méthodes comme des catégories au lieu de droite sur la métaclasse. De cette façon, vous pouvez définir une fois pour toutes les collections:

class PropertyMapCategory { 
    static Map mapProperty(Collection c, String keyParam, String valParam) { 
     return c.inject([:]) { memo, entry -> 
      memo[entry[keyParam]] = entry[valParam] 
      return memo 
     } 
    } 
} 

Exemple d'utilisation:

use(PropertyMapCategory) { 
    println columns.mapProperty('name', 'val') 
} 
+0

Je suppose qu'il est nommé injecter dans Groovy car il pourrait avoir été inspiré par injecter: dans: dans Smalltalk: | somme de liste | liste: = OrderedCollection new ajouter: 1; ajouter: 2; ajouter: 3; toi même. sum: = liste injecter: 0 dans: [: a: b | a + b]. Transcription cr; spectacle: somme "prints 6" – OlliP

13

était la méthode groupBy pas disponible lorsque cette question a été posée?

+0

On dirait que non - c'est depuis le 1.8.1, 2011. La question a été posée en 2008. Mais dans tous les cas, groupBy est maintenant la voie à suivre. – mvmn

3

Si vous avez besoin d'une simple paire clé-valeur, la méthode collectEntries devrait suffire. Par exemple

def names = ['Foo', 'Bar'] 
def firstAlphabetVsName = names.collectEntries {[it.charAt(0), it]} // [F:Foo, B:Bar] 

Mais si vous voulez une structure similaire à un Multimap, dans lequel il y a plusieurs valeurs par clé, alors vous voudriez utiliser la méthode groupBy

def names = ['Foo', 'Bar', 'Fooey'] 
def firstAlphabetVsNames = names.groupBy { it.charAt(0) } // [F:[Foo, Fooey], B:[Bar]] 
Questions connexes