How to iterate over a map created in MVEL - mvel

I have created a map in MVEL and I have to iterate over it using foreach. How would I do that?
There is a similar question:
How to iterate over a map in mvel
But in that case the map was created in Java and had a method to return array of keys (entrySet) which is not the case with me.
//MVEL
map = [
'a': 'a1',
'b': 'b2',
'c': 'c3'
];
foreach (key: map) {
System.out.println(key);
}
I have tried both map and map.entrySet in the foreach loop but none seems to work.
Note: I test it using MVEL command line and using MVEL version 2.2.0.15

Although you have accepted an answer, I think it is better to add something as not to mislead other people:
... had a method to return array of keys (entrySet) which is not the case with me
First, Map is a Map. Map created in MVEL is simply a "Java" Map. The way to iterate is just the same and they are providing same methods
Second, entrySet() is not returning "array of keys". It is returning a Set of Entry (as its name suggests).
I am not sure why you cannot use entrySet as it works just fine for me. I suspect you have do foreach (e : map.entrySet). That will not work, because in MVEL, property navigation can mean several thing, like bean properties (which means it will call map.getEntrySet()), or looking up a map (which means it will call map.get('entrySet')), or getting the field (which means 'map.entrySet'). However all these are not valid for your case. You simply want to invoke map.entrySet() method so that you should just do foreach (e : map.entrySet())
The proper way to do is something like this:
map = ['a':'a1', 'b':'b1'] ;
foreach(entry : map.entrySet()) {
System.out.println('key ' + entry.key + ' value ' + entry.value)
};

You can use something like this:
map = [
'a': 'a1',
'b': 'b2',
'c': 'c3'
];
foreach (key : map.keySet()) {
System.out.println("Key:" + key + " Value:" + map[key]);
}
It outputs:
Key:b Value:b2
Key:c Value:c3
Key:a Value:a1

Related

How to get size of specfic value inside array Kotlin

here is example of the list. I want to make dynamic where maybe the the value will become more.
val list = arrayListOf("A", "B", "C", "A", "A", "B") //Maybe they will be more
I want the output like:-
val result = list[i] + " size: " + list[i].size
So the output will display every String with the size.
A size: 3
B size: 2
C size: 1
If I add more value, so the result will increase also.
You can use groupBy in this way:
val result = list.groupBy { it }.map { it.key to it.value.size }.toMap()
Jeoffrey's way is better actually, since he is using .mapValues() directly, instead of an extra call to .toMap(). I'm just leaving this answer her since
I believe that the other info I put is relevant.
This will give a Map<String, Int>, where the Int is the count of the occurences.
This result will not change when you change the original list. That is not how the language works. If you want something like that, you'd need quite a bit of work, like overwriting the add function from your collection to refresh the result map.
Also, I see no reason for you to use an ArrayList, especially since you are expecting to increase the size of that collection, I'd stick with MutableList if I were you.
I think the terminology you're looking for is "frequency" here: the number of times an element appears in a list.
You can usually count elements in a list using the count method like this:
val numberOfAs = list.count { it == "A" }
This approach is pretty inefficient if you need to count all elements though, in which case you can create a map of frequencies the following way:
val freqs = list.groupBy { it }.mapValues { (_, g) -> g.size }
freqs here will be a Map where each key is a unique element from the original list, and the value is the corresponding frequency of that element in the list.
This works by first grouping elements that are equal to each other via groupBy, which returns a Map<String, List<String>> where each key is a unique element from the original list, and each value is the group of all elements in the list that were equal to the key.
Then mapValues will transform that map so that the values are the sizes of the groups instead of the groups themselves.
An improved approach, as suggested by #broot is to make use of Kotlin's Grouping class which has a built-in eachCount method:
val freqs = list.groupingBy { it }.eachCount()

Kotlin sorting Mutable map of strings

Why cannot I sort a mutable map of string. My map is declared as follows.
val schedule: MutableMap<String, ArrayList<String>>
It gives me schedule object as follows.
{1=[1], 0=[0], 3=[3], 2=[2], 5=[5], 4=[4, 14.07, 16.07, 01.08, 10.08], 6=[6]}
Now for day 4, I would to sort the elements in ascending order, ideally ignoring first element. I want my output to look like below.
{1=[1], 0=[0], 3=[3], 2=[2], 5=[5], 4=[4, 1.08, 10.08, 14.07, 16.07], 6=[6]}
I can access the required day with schedule.schedule["4"]?.sorted()
but this doesn't do anything. I tired converting Strings to Ints but still no luck.
Use sort() instead of sorted().
sort() sorts "in place": it mutates the ArrayList
sorted() returns a new sorted ArrayList
Try it: https://repl.it/repls/BitterRapidQuark
val map = mutableMapOf("4" to arrayListOf<String>("4", "14.07", "16.07", "01.08", "10.08"))
println("Original: " + map) // {4=[4, 14.07, 16.07, 01.08, 10.08]}
map["4"]?.sorted()
println("Not mutated: " + map) // {4=[4, 14.07, 16.07, 01.08, 10.08]}
map["4"]?.sort()
println("Mutated: " + map) // {4=[01.08, 10.08, 14.07, 16.07, 4]}

Groovy maps with Integer keys - 'getAt' in DefaultGroovyMethods cannot be applied to (java.lang.Integer)

I've just started to practice Groovy and I have a question related to maps and IDEA IDE.
Why IDEA shows me the notification below when I try to use Integer as a key for a map? This simple Groovy script works fine and print correct result.
list = [4, 7, 3, 7, 7, 1, 4, 2, 4, 2, 7, 5]
map = [:]
list.each {
t = map[(it)]
map[(it)] = t != null ? t + 1 : 1
}
map.each {key, value -> if (value == 1) println key}
It is caused because IntelliJ IDEA sees map variable as Object - it seems like IDEA does not follow type inference if static type or keyword def is missing in front of the variable. If you take a look at DefaultGroovyMethods you will see that there is only one method getAt implemented for Object type:
public static Object getAt(Object self, String property) {
return InvokerHelper.getProperty(self, property);
}
This is why IDEA warns you about missing method getAt(Object self, Integer property) because it is not aware that map is actually a Map and not an Object.
Please follow the official Groovy's guideline that says:
Variables can be defined using either their type (like String) or by using the keyword def:
String x
def o
Source: http://docs.groovy-lang.org/latest/html/documentation/core-semantics.html#_variable_definition
If you define your variable as
def map = [:]
IntelliJ wont complain anymore.

Google app script : New variable keeps reference of source variable

We have variable "a" and we want to create variable "b" as a mirror of "a" variable and then change one of its elements.
Code
function h(){
var a=[[1,2,3]]
var b=a;
b[0][0]="test"
Logger.log(b)
Logger.log(a)
}
Output
[[test,2,3]]
[[test,2,3]]
Why is this happening? Any way to avoid this?
This referes to another question:
Copying array by value in JavaScript
You may test some suggested solutions. I've tested this answer:
https://stackoverflow.com/a/23245968/5372400
The code:
function h(){
var a=[[1,2,3]];
var b = JSON.parse(JSON.stringify(a));
b[0][0]="test";
Logger.log(b);
Logger.log(a);
}
result is
[[test, 2, 3]]
[[1.0, 2.0, 3.0]
Looks like, javascript like c does not do array assignments.
You have to deep copy value in array b for this you can use slice method :
arr2 = arr1.slice();
Below is your code with some modifications:
function h(){
var a=[1,2,3];
var b= a.slice();
b[0]="test";
console.log(b);
console.log(a);
}

How to check if value is a map or a string in apache velocity

I use apache velocity to render templates. Now, the problem is that I have an API data feed with values that can either contain a map or a string. I iterate through the list and put values in a table. This is a sample of the feed am getting:
{
"Secondary Camera":
{
"Key1":
{
"present": "true",
"value": "2 Megapixel"
},
"Key2":
{
"present": "true","value": "0.3 Megapixel"
}
}
},
{
"Other Camera Features":
{
"Key1": "Auto Focus, Panorama, Photo Sphere, Lens Blur",
"Key2": "Panorama Shot"
}
},
With this the Key1/Key2 values are sometimes string and sometimes they are a map. Is there a fool proof way to make this work with strict mode on ?
There is a way. the main idea is to directly inspect values classes.
The following code
#set(
$map = {
'key1' : 'string_value',
'key2' : [ 'array', 'value' ],
'key3' : { 'map' : 'value' }
}
)
#set ( $obj = '' ) ## dummy object
#set ( $string_class = $obj.class.forName('java.lang.String') )
#set ( $map_class = $obj.class.forName('java.util.Map') )
#foreach( $value in $map )
value class name = $value.class.name
#if ( $string_class.isAssignableFrom($value.class) )
value is a string
#elseif ( $map_class.isAssignableFrom($value.class) )
value is a map
#end
#end
will produce:
value class name = java.lang.String
value is a string
value class name = java.util.LinkedHashMap
value is a map
You can also test directly if an object is a class with $obj.class.name == 'java.lang.String', but you cannot do it for maps this way, since java.util.Map is only a root abstract interface for all map classes.
Warning: some Velocity configurations (known as secure) will forbid the access to the class property of objects.
While trying to get a generic VTL template for APIGateway, I came to this condition that works in this particular environment. Apparently when toString() is applied to a Map this string is printed "[object Object]", so we can use this trick to distinguish between a Map and other types. This doesn't require access to "class". It doesn't feel particularly robust to rely on this hack, but so far is the best I got. So I'm sharing here for the record.
#if ($cdrField.toString() != "[object Object]")
I tried #claude-brisson's answer, but was unable to make that work with AWS AppSync. My approach is less sophisticated, and I hate that it checks an object's class by its string name, but it gets the job done.
#foreach( $entry in $ctx.args.entrySet() )
#if( $entry.value.class.name == "java.util.LinkedHashMap" )
##Do something if value is a map
#elseif( $entry.value.class.name == "java.lang.String" )
##Do something if value is a string
#elseif( $entry.value.class.name == "java.util.ArrayList" )
##Do something if value is a list
#end
#end
Where $ctx.args is a map of input arguments.