I'm creating a map like this:
def myMap = [:]
The map is basically an object for a key and an int for a value. When I iterate over the map, I decret the value, and if it's 0, I remove it. I already tried myMap.remove(), but I get a ConcurrentModificationError - which is fair enough. So I move on to using it.remove(), which is giving me weird results.
Basically, my code is this:
myMap.each {
it.value--;
if( it.value <= 0 )
it.remove();
}
Simple enough. My problem is, if I print myMap.size() before and after the remove, they're the same. If I call myMap.containsKey( key ), it gives me true, the key is still in there.
But, if I print out the map like this:
myMap.each { System.out.println( "$it.key: $it.value" ); }
I get nothing, and calling myMap.keySet() and myMap.values() return empty.
Anyone know what's going on?
This should be a bit more efficient than Tim's answer (because you only need to iterate over the map once). Unfortunately, it is also pretty verbose
def map = [2:1, 3:4]
def iterator = map.entrySet().iterator()
while (iterator.hasNext()) {
if (iterator.next().value - 1 <= 0) {
iterator.remove()
}
}
// test that it worked
assert map == [3:4]
Can you do something like this:
myMap = myMap.each { it.value-- }.findAll { it.value > 0 }
That will subtract one from every value, then return you a new map of only those entries where the value is greater than zero.
You shouldn't call the remove method on a Map Entry, it is supposed to be a private method used internally by the Map (see line 325 for the Java 7 implementation), so you calling it yourself is getting the enclosing Map into all sorts of bother (it doesn't know that it is losing entries)
Groovy lets you call private methods, so you can do this sort of trickery behind the back of the Java classes
Edit -- Iterator method
Another way would be:
myMap.iterator().with { iterator ->
iterator.each { entry ->
entry.value--
if( entry.value <= 0 ) iterator.remove()
}
}
Related
I want convert string to Map in grails. I already have a function of string to map conversion. Heres the code,
static def StringToMap(String reportValues){
Map result=[:]
result=reportValues.replace('[','').replace(']','').replace(' ','').split(',').inject([:]){map,token ->
List tokenizeStr=token.split(':');
tokenizeStr.size()>1?tokenizeStr?.with {map[it[0]?.toString()?.trim()]=it[1]?.toString()?.trim()}:tokenizeStr?.with {map[it[0]?.toString()?.trim()]=''}
map
}
return result
}
But, I have String with comma in the values, so the above function doesn't work for me. Heres my String
[program_type:, subsidiary_code:, groupName:, termination_date:, effective_date:, subsidiary_name:ABC, INC]
my function returns ABC only. not ABC, INC. I googled about it but couldnt find any concrete help.
Generally speaking, if I have to convert a Stringified Map to a Map object I try to make use of Eval.me. Your example String though isn't quite right to do so, if you had the following it would "just work":
// Note I have added '' around the values.
String a = "[program_type:'', subsidiary_code:'', groupName:'', termination_date:'', effective_date:'', subsidiary_name:'ABC']"
Map b = Eval.me(a)
// returns b = [program_type:, subsidiary_code:, groupName:, termination_date:, effective_date:, subsidiary_name:ABC]
If you have control of the String then if you can create it following this kind of pattern, it would be the easiest solution I suspect.
In case it is not possible to change the input parameter, this might be a not so clean and not so short option. It relies on the colon instead of comma values.
String reportValues = "[program_type:, subsidiary_code:, groupName:, termination_date:, effective_date:, subsidiary_name:ABC, INC]"
reportValues = reportValues[1..-2]
def m = reportValues.split(":")
def map = [:]
def length = m.size()
m.eachWithIndex { v, i ->
if(i != 0) {
List l = m[i].split(",")
if (i == length-1) {
map.put(m[i-1].split(",")[-1], l.join(","))
} else {
map.put(m[i-1].split(",")[-1], l[0..-2].join(","))
}
}
}
map.each {key, value -> println "key: " + key + " value: " + value}
BTW: Only use eval on trusted input, AFAIK it executes everything.
You could try messing around with this bit of code:
String tempString = "[program_type:11, 'aa':'bb', subsidiary_code:, groupName:, termination_date:, effective_date:, subsidiary_name:ABC, INC]"
List StringasList = tempString.tokenize('[],')
def finalMap=[:]
StringasList?.each { e->
def f = e?.split(':')
finalMap."${f[0]}"= f.size()>1 ? f[1] : null
}
println """-- tempString: ${tempString.getClass()} StringasList: ${StringasList.getClass()}
finalMap: ${finalMap.getClass()} \n Results\n finalMap ${finalMap}
"""
Above produces:
-- tempString: class java.lang.String StringasList: class java.util.ArrayList
finalMap: class java.util.LinkedHashMap
Results
finalMap [program_type:11, 'aa':'bb', subsidiary_code:null, groupName:null, termination_date:null, effective_date:null, subsidiary_name:ABC, INC:null]
It tokenizes the String then converts ArrayList by iterating through the list and passing each one again split against : into a map. It also has to check to ensure the size is greater than 1 otherwise it will break on f[1]
Please help with a metaprogramming configuration such that I can add collections methods called collectWithIndex and injectWithIndex that work in a similar manner to eachWithIndex but of course include the base functionality of collect and inject. The new methods would accept a two (three with maps) argument closure just like eachWithIndex. I would like to have the capability to utilize these methods across many different scripts.
Use case:
List one = [1, 2, 3]
List two = [10, 20, 30]
assert [10, 40, 90] == one.collectWithIndex { value, index ->
value * two [index]
}
Once the method is developed then how would it be made available to scripts? I suspect that a jar file would be created with special extension information and then added to the classpath.
Many thanks in advance
I'm still sure, it's not a proper SO question, but I'll give you an example, how you can enrich metaclass for your multiple scripts.
Idea is based on basescript, adding required method to List's metaClass in it's constructor. You have to implement collect logic yourself, through it's pretty easy. You can use wrapping
import org.codehaus.groovy.control.CompilerConfiguration
class WithIndexInjector extends Script {
WithIndexInjector() {
println("Adding collectWithIndex to List")
List.metaClass.collectWithIndex {
int i = 0
def result = []
for (o in delegate) // delegate is a ref holding initial list.
result << it(o, i++) // it is closure given to method
result
}
}
#Override Object run() {
return null
}
}
def configuration = new CompilerConfiguration()
configuration.scriptBaseClass = WithIndexInjector.name
new GroovyShell(configuration).evaluate('''
println(['a', 'b'].collectWithIndex { it, id -> "[$id]:$it" })
''')
// will print [[0]:a, [1]:b]
If you like to do it in more functional way, without repeating collect logic, you may use wrapping proxy closure. I expect it to be slower, but maybe it's not a deal. Just replace collectWithIndex with following implementation.
List.metaClass.collectWithIndex {
def wrappingProxyClosure = { Closure collectClosure, int startIndex = 0 ->
int i = startIndex
return {
collectClosure(it, i++) // here we keep hold on outer collectClosure and i, and use call former with one extra argument. "it" is list element, provided by default collect method.
}
}
delegate.collect(wrappingProxyClosure(it))
}
offtopic: In SO community your current question will only attract minuses, not answers.
I'm trying to get the first K elements from a sorted map by the following piece of code:
//return top rank k elements
public static LinkedHashMap<String,Double> getTopRank(int i){
//store top k elements
LinkedHashMap<String, Double> result=new LinkedHashMap<>();
int count=0;
//use the static rankMap in the class
rankMap.each {key,value->
result.put(key, value);
count++;
if(count>=i){
println "Time to return"
return result;
}
}
//in case the loop does not work
return result;
}
What I expect is that when the result Map already has a size of i elements, the method will return, giving me a i-size sorted map. Note that rankMap stores the elements in a certain order I want, and its size is far bigger than int i I pass to the method.
And I'm calling the method by
LinkedHashMap<String,Double> content=getTopRank(outputSize);
But unexpectedly finally the content had the size of rankMap rather than i! And in the console I saw hundreds of Time to return lines. The line return result was executed again and again until it reached the end of rankMap.
I'm pretty sure that the line getTopRank(outputSize) was not in a loop. Then it seems strange to me why this method can return multiple times without ending. Is it caused by my putting return statement in the closure?
Please advise or tell me how this is true in Groovy. One step further, how can I get only first k elements from a sorted map then?
You misunderstood key concepts of Groovy.
The only way to finish the each() execution before reaching the end, is to throw an exception. If your want to exit the loop conditionally, use standard loop types like for or while:
int count=0
def result = [:]
for( def e in rankMap ){
count++
result[ e.key ] = e.value
if( i <= count ) return result
}
The method itself is not returning. each is a method which receives a closure. Closures have their own returning context, which is not tied to the method who invoked them, thus, the loop is not broken.
I'd like to suggest getting a range from the map's entrySet and collecting the resulting entries:
def getTopRank(int i) {
rankMap
.entrySet()
.toList()[0..<i]
.collectEntries()
}
rankMap = [
'Beatles' : 'The White Album',
'Pink Floyd' : 'The Dark Side of the Moon',
'Rolling Stones' : 'Sticky Fingers',
'The Doors' : 'Morrison Hotel',
'Bob Dylan' : 'Bob Dylan'
]
assert getTopRank(2) == [
'Beatles' : 'The White Album',
'Pink Floyd' : 'The Dark Side of the Moon']
assert getTopRank(4) == [
'Beatles' : 'The White Album',
'Pink Floyd' : 'The Dark Side of the Moon',
'Rolling Stones' : 'Sticky Fingers',
'The Doors' : 'Morrison Hotel',]
public static LinkedHashMap<String,Double> getTopRank(int i){
rankMap.take(i)
}
http://www.groovy-lang.org/gdk.html
http://docs.groovy-lang.org/latest/html/groovy-jdk/java/util/Map.html#take(int)
I need a thread safe data structure with three operations: remove, getRandom, reset.
I have only two ideas by now.
First: Seq in syncronized var.
val all: Array[String] = ... //all possible.
var current: Array[String] = Array.empty[String]
def getRandom(): = {
val currentAvailable = current
currentAvailable(Random.nextInt(currentAvailable.length))
}
def remove(s: String) = {
this.syncronized {
current = current diff Seq(s)
}
}
def reset(s: String) = {
this.syncronized {
current = all
}
}
Second:
Maintain some Map[String,Boolean], there bool is true when element currently is present. The main problem is to make a fast getRandom method (not something like O(n) in worst case).
Is there a better way(s) to implement this?
Scala's Trie is a lock free data structure that supports snapshots (aka your currentAvailable) and fast removals
Since I'm not a Scala expert so this answer is general as an example I used Java coding.
in short the answer is YES.
if you use a map such as :
Map<Integer,String> map=new HashMap<Integer,String>(); //is used to get random in constant time
Map<String,Integer> map1=new HashMap<String,Integer>(); //is used to remove in constant time
to store date,
the main idea is to keep the key( in this case the integer) synchronized to be {1 ... size of map}
for example to fill this structure, you need something like this:
int counter=0; //this is a global variable
for(/* all your string (s) in all */ ){
map.put(counter++, s);
}
//then , if you want the removal to be in constant time you need to fill the second map
for(Entry e : map.EntrySet(){
map1.put(e.getValue(),e.getKey());
}
The above code is the initialization. everytime you want to set things you need to do that
then you can achieve a random value with O(1) complexity
String getRandom(){
int i; /*random number between 0 to counter*/
return map.get(i);
}
Now to remove things you use map1 to achive it in constant time O(1);
void remove(String s){
if(!map1.containsKey(s))
return; //s doesn't exists
String val=map.get(counter); //value of the last
map.remove(counter) //removing the last element
int thisCounter= map1.get(s); //pointer to this
map1.remove(s); // remove from map1
map.remove(counter); //remove from map
map1.put(thisCounter,val); //the val of the last element with the current pointer
counter--; //reducing the counter by one
}
obviously the main issue here is to keep the synchronization ensured. but by carefully analyzing the code you should be able to do that.
I have this code:
def input1 = ['a','b','e','r','t']
input2 = ['v','n','m','y']
ans = []
def common(def element,def i) {
if (element == input2[i]) {
ans << element
return
} else {
common(element,++i)
}
}
for (i=0;i<input1.size();i++) {
common(input1[i],0)
}
which is generating Stack Overflow error. Why is this happening?
Edit:
I'm trying to create my own way of finding common element between two lists.
You never check if i is greater than the length of input2, and in Groovy, getting beyond the length of a List returns null
So on the first element, it will keep looping round
if (element == input2[i]) {
for ever-increasing values of i, calling the common function every time, as it never matches a
Guessing at what you are trying to do, this can all be re-written as:
def input1 = ['a','b','e','r','t']
def input2 = ['v','n','m','y']
def ans = input1.intersect( input2 )
But it's hard to be sure what you want, and you dont explicitly say.
Edit
One method of deep recursion that avoids Stack Overflows is to use Groovy's trampoline method.
def common
common = { Object element, Collection list ->
if( list.size() == 0 ) { // element not found. Return null
null
}
else if( list.head() == element ) { // element found. Return it
element
}
else {
common.trampoline( element, list.tail() ) // Trampoline down the list and check again
}
}
common = common.trampoline()
def elements = ['a','b','e','v','r','t'].collect { // For each element in this list
common( it, ['v','n','m','y'] ) // Find if it's in our other list
}.findAll() // And remove the nulls
assert elements == [ 'v' ]
But I'd still use intersect in this case, the above is just to show one of Groovy's ways you can avoid too-deep recursion...
The problem is that your code doesn't stop when reaches the end of array input2. If element is not in input2 then it will keep making recursive calls common(element,++i) forever which results in stack overflow error.