Create a map from existing variables (e.g. a Java equivalent to JavaScript's `{varA, varB, varC}`)

Viewed 121

I'm primarily a JavaScript developer, but am currently working on some Groovy code and haven't been able to figure out how to do something that's super-simple in JavaScript.

The JavaScript equivalent of what I'm trying to do follows.

I'm specifically trying to figure out the Java (or Groovy) equivalent of creating an object in JS (map in Java) out of just the existing variable names, e.g. {a, b, c} shorthand in the code snippet below. Any guidance will be much appreciated!

javaScriptExample()

function javaScriptExample () {
  // the variables already exist in the program that I'm working in
  const a = 'a'
  const b = 'bee'
  const c = 'see'
  
  //                ➡️ Here's where I'm stuck. ⬅️  
  // I simply want to be able to arbitrarily pass variable keys and values
  // as a map to another function, _using just the variable keys_,
  // e.g. the equivalent of JavaScript's `{a, b, c}` in the next call
  doOtherStuffWithVariables({a, b, c})
}

function doOtherStuffWithVariables (obj) {
  for (const key in obj) {
    console.log(`variable ${key} has a value of "${obj[key]}" and string length of ${obj[key].length}`)
  }
}

4 Answers

As stated, there is no shortcut in Groovy. But if you want/need this syntax, you can achieve this with Groovy Macros.

E.g. a very straight forward attempt:

@Macro
static Expression map(MacroContext ctx, final Expression... exps) {
    return new MapExpression(
        exps.collect{
            new MapEntryExpression(GeneralUtils.constX(it.getText()), it)
        }
    )
}

Usage:

def a = 42
def b = 666

println(map(a,b))
// → [a:42, b:666]

Unfortunately there's no direct counterpart in Groovy or Java for object deconstruction in JS. Although you can use map literals to write a similar code:

def doStuff( Map obj ) {
  obj.each{ key, val -> 
    println "variable $key has a value of '${val}' and string length of ${obj[key].size()}" 
  }
}

def example(){
  def a = 'a'
  def b = 'bee'
  def c = 'see'

  doStuff( a:a, b:b, c:c )
}

I am not sure what you exactly want to do, but afaik, there is nothing similiar in java.

You could use tho the "new" methods in the java collections library:

    String a = "a";
    String b = "b";
    String c = "c";

    Map.of("a", a, "b", b, "c", c);
    List.of(a,b,c)

But you should keep in mind, those methods are returning unmodifiable objects.

JavaDoc: Returns an unmodifiable list containing three elements. See Unmodifiable Lists for details.

As already said, this is not possible in Groovy, because as soon as you use a variable a in your code, then the value of that variable is passed and the name is forgotten.
However, I have also a solution which involves an intermediate object holding the properties to be interrogated:

def toMap(obj) {
    obj.properties.collectEntries {
        [(it.key):it.value]
    }
}

def ex =
  new Expando().tap {
    a = 'A'
    b = 'B'
  }

assert toMap(ex) == [a:'A', b:'B']

The involves the dynamically expandable object feature.

Related