kotlin adding pair to map when value is a list - dictionary

I know this is basic but can I do this in a shorter way:
val ss = mutableMapOf<String, MutableList<String>>()
if(ss["new_key"] != null){
ss["new_key"]!!.add("NEW")
}
else{
ss["new_key"] = mutableListOf("OLD")
}
This basically checks if the key exists in the map
if it does an element is appended to the list(value) otherwise a new key-value pair is created
Can't I create a new key on the go? like this:
ss["new_key"].add("OLD")
ss["new_key"].add("NEW")

You have at least 2 options:
use computeIfAbsent:
ss.computeIfAbsent("new_key") { mutableListOf() } += "NEW"
use getOrPut:
ss.getOrPut("new_key", ::mutableListOf) += "NEW"

Related

How to add any symbols after prefix_?

is there any solution? e.g. I have data in Map with key favorites_ prefix and values _suffix (for example: favorites_jeans, favorites_suit,...,). I want to by dint of loop get that values and set in List, because of it I must give keys of map, right?
I want to know how can I get values of myMap["favorites_*"] (* - after the favorites_ any symbols).
List<String> favoritesStrings = ['favorite_name','favorite_jeans',];
Map<String,dynamic> myMap = {
favoritesStrings[0]:'0',
favoritesStrings[1]:'1',
'someKey':'2',
'anotherKey':'3',
};
favoritesStrings.forEach((favorite)=>print(myMap[favorite]));//prints 0 1
As per what I understood, you want to fetch value from map using "favorites_" + a dynamic value from list as key.
You just have to use String templates and use $ to insert suffix variable to build key dynamically:
List<String> suffixList = ["jeans", "suit", "shirt"];
for(String suffix in suffixList) {
var item = myMap["favorites_$suffix"];
// Do something with item
}
Hope it helps

How to add new item in specific index?

I new in kotlin , i want to update an item in lists.
I use this code:
var index: Int
for (record in recordList)
if (record.id == updatedHeader?.id) {
index = recordList.indexOf(record)
recordList.add(index, updatedHeader)
}
but it cant do this, because of ConcurrentModificationException
Assuming that recordList is a MutableList and val (so, you'd like to modify the records in place), you can use forEachIndexed to find the records you care about and replace them.
This did not cause a ConcurrentModificationException:
recordList.forEachIndexed { index, record ->
if(record.id == updatedHeader?.id) recordList[index] = updatedHeader
}
On the other hand, if you redefine recordList as a non-mutable list, and a var, you could rewrite the entire list using map:
recordList = recordList.map { if(it.id == updatedHeader?.id) updatedHeader else it }
Of course, you could call .toMutableList() on the end of that if you wanted to turn your List into a MutableList.
If there's a single record with the given id in the list, you can find its index and add the header at that index:
val index = recordList.indexOfFirst { it.id == updatedHeader.id }
if (index >= 0)
recordList.add(index, updatedHeader)
If there are multiple records with the given id and you want to prepend header before each of them, you can use get listIterator and use its methods to modify the list during the iteration without getting ConcurrentModificationException:
val iterator = recordList.listIterator()
for (record in iterator) {
if (record.id == updatedHeader.id) {
iterator.previous() // move to the position before the record
iterator.add(updatedHeader) // prepend header
iterator.next() // move next, back to the record
}
}

How should I structure my data so it works with firebase?

I want to update some data in the forms of
wordBank = {
{word:"aprobi", translation:"to approve", count:2},
{word:"bati", translation:"to hit, to beat, to strike", count:1},
{word:"da", translation:"of", count:1}
}
the goal is to able to extract and display all the values of all the keys in each JSON object. How do I create this format on firebase? do I use .update? or something else?
currently I could only get firebase .update() to work with an array but it gives me data like this
wordBank = [
{word:"aprobi", translation:"to approve", count:2},
{word:"bati", translation:"to hit, to beat, to strike", count:1},
{word:"da", translation:"of", count:1}
];
where each word-object is an index in the array.
Here's how I construct my wordObjects:
function getWords() {
if (document.getElementsByClassName("vortarobobelo").length != 0){
var words;
words = document.getElementsByClassName("vortarobobelo")[0].children[0].children;
for (var i =0; i < words.length; i++) {
var localBank = {} //creating the local variable to store the word
var newWord = words[i].children[0].innerText; // getting the word from the DOM
var newTranslation = words[i].children[1].innerText; // getting the translation from the DOM
localBank.word = newWord;
localBank.translation = newTranslation;
localBank.count = 0 //assuming this is the first time the user has clicked on the word
console.log(localBank);
wordBank[localBank.word] = localBank;
fireBank.update(localBank);
}
}
}
If you want to store the items within an object, you need to pick keys to store them against.
You can't store unkeyed values inside an object in Javascript. This would result in a syntax error:
wordBank = {
{word:"aprobi", translation:"to approve", count:2},
{word:"bati", translation:"to hit, to beat, to strike", count:1},
{word:"da", translation:"of", count:1}
}
The other option is to store them in an array, in which case the keys will be automatically assigned as array indices. Just like your second example.
Maybe you want to store the word objects, using the word itself as a key?
wordBank = {
aprobi: {word:"aprobi", translation:"to approve", count:2},
bati: {word:"bati", translation:"to hit, to beat, to strike", count:1},
da: {word:"da", translation:"of", count:1}
}
This would be easy to do with Firebase. Let's say you have all of your word objects as a list.
var ref = new Firebase("your-firebase-url");
wordObjects.forEach(function(wordObject) {
ref.child(wordObject.word).set(wordObject);
});
Or you could create the object with Javascript, then add it to Firebase using .update.
var wordMap = {};
wordObjects.forEach(function(wordObject) {
wordMap[wordObject.word] = wordObject;
});
ref.update(wordMap);

How do I add multiple Groovy map entries without overwriting the current entries?

My question with Groovy Maps. I've been searching for a way to programmatically add a new entry to a Groovy map without overwriting the current entry. For example
def editsMap = [:]
lineEdits.flag.each
{ lineEdits_Flag ->
editsMap.put('FlagId',lineEdits_Flag.id)
editsMap.put('FlagMnemonic',lineEdits_Flag.mnemonic)
editsMap.put('Action',lineEdits_Flag.action)
println "editsMap: ${editsMap}"
}
The first pass produces this map:
editsMap: [FlagId:10001, FlagMnemonic:TRA, Action:review]
But the second pass overwrites the first pass with:
editsMap: [FlagId:10002, FlagMnemonic:REB, Action:deny]
What I'm trying to do is create multiple entries within the one map. I need my map to populate something like this:
editsMap: [FlagId:10001, FlagMnemonic:TRA, Action:review]
editsMap: [FlagId:10002, FlagMnemonic:REB, Action:deny]
editsMap: [FlagId:10003, FlagMnemonic:UNB, Action:deny]
editsMap: [FlagId:20001, FlagMnemonic:REB, Action:deny]
editsMap: [FlagId:20002, FlagMnemonic:ICD, Action:review]
editsMap: [FlagId:30001, FlagMnemonic:REB, Action:deny]
editsMap: [FlagId:40001, FlagMnemonic:ICD, Action:review]
editsMap: [FlagId:40002, FlagMnemonic:MPR, Action:review]
editsMap: [FlagId:50001, FlagMnemonic:CPT, Action:deny]
editsMap: [FlagId:60001, FlagMnemonic:DTU, Action:deny]
editsMap: [FlagId:70001, FlagMnemonic:ICD, Action:review]
editsMap: [FlagId:70002, FlagMnemonic:MPR, Action:review]
Once I have populated my map then I need to be able to find certain values in order to process a message. I believe that I can use something like:
def thisValue = appliedEditsMap[FlagId, '10001'] ?: "default"
to do a quick lookup.
Can someone help me understand how to programmatically add values to a Groovy map without overwriting the values already in the map?
You want something like Guava's MultiMap:
Multimap<String, String> myMultimap = ArrayListMultimap.create();
// Adding some key/value
myMultimap.put("Fruits", "Bannana");
myMultimap.put("Fruits", "Apple");
myMultimap.put("Fruits", "Pear");
myMultimap.put("Vegetables", "Carrot");
// Getting values
Collection<string> fruits = myMultimap.get("Fruits");
System.out.println(fruits); // [Bannana, Apple, Pear]
This guy makes a pure Groovy emulation of Multimap:
class GroovyMultimap {
Map map = [:]
public boolean put(Object key, Object value) {
List list = map.get(key, [])
list.add(value)
map."$key" = list
}
}
You can use putAt and getAt for syntatic sugar in map operations. You can also try a mixin in a map object.
He also uses Groovy with Guava's multimap:
List properties = ['value1', 'value2', 'value3']
Multimap multimap = list.inject(LinkedListMultimap.create()) {
Multimap map, object ->
properties.each {
map.put(it, object."$it")
}
map
}
properties.each {
assertEquals (multimap.get(it), list."$it")
}
I came across this several years ago as an answer to a similar question on another site. I can't find where it originally came from so if anyone knows the source please post it here.
LinkedHashMap.metaClass.multiPut << { key, value ->
delegate[key] = delegate[key] ?: []; delegate[key] += value
}
def myMap = [:]
myMap.multiPut("a", "1")
myMap.multiPut("a", "2")
myMap.multiPut("a", "3")
myMap.each {key, list ->
println '${key} -> ${list}'
}
Gives:
a -> 1,2,3
The use of the injected multiPut() method does the magic.
You could also do something like this:
// Dummy map for testing
lineEdits = [ flag:[
[id:10001, mnemonic:'TRA', action:'review'],
[id:10002, mnemonic:'REB', action:'deny'],
[id:10003, mnemonic:'UNB', action:'deny'],
[id:20001, mnemonic:'REB', action:'deny'],
[id:20002, mnemonic:'ICD', action:'review'],
[id:30001, mnemonic:'REB', action:'deny'],
[id:40001, mnemonic:'ICD', action:'review'],
[id:40002, mnemonic:'MPR', action:'review'],
[id:50001, mnemonic:'CPT', action:'deny'],
[id:60001, mnemonic:'DTU', action:'deny'],
[id:70001, mnemonic:'ICD', action:'review'],
[id:70002, mnemonic:'MPR', action:'review'] ] ]
def editsMap = lineEdits.flag
.groupBy { it.id } // Group by id
.collectEntries { k, v ->
[ k, v[ 0 ] ] // Just grab the first one (flatten)
}
assert editsMap[ 60001 ] == [ id:60001, mnemonic:'DTU', action:'deny' ]
If you want to do the multimap thing without external classes, you can just store a map of lists instead, the syntax won't be cumbersome or anything.
def editsMap = [:].withDefault{[]}
lineEdits.flag.each
{
lineEdits_Flag ->
editsMap.FlagId << lineEdits_Flag.id
editsMap.FlagMnemonic << lineEdits_Flag.mnemonic
editsMap.Action << lineEdits_Flag.action
println "editsMap: ${editsMap}"
}
or if you really preferred your original syntax it would look like:
editsMap.get('FlagId').add(lineEdits_Flag.id)
or even this should work:
editsMap.get('FlagId') << lineEdits_Flag.id
The advantage of this solution is that it tends to be more obvious what you are doing... for instance it's not a magic map that converts single items to a list (which is not the standard map contract) but it's always a map of lists that you simply use as a map of lists.
The .get will always work the same way the multimap was described--it will always return the list for that item in the map.
A map is a set of key-value mappings, you plug in different values by key so that you can use the key to find them later. Your example is plugging in values for the same keys over and over. You need to pick unique keys.
Make some class to store your values for one entry in the map:
class Stuff {
String flagMnemonic
String action
}
Make a map where you will use flagId as the key (because that's how you identify the flag uniquely) and Stuff as the value (because it's the data you want to lookup).
def editsMap = [:]
If you used type declarations here, and if flagId is a String, the map's type would be Map<String, Stuff>.
Now you can put stuff in the map:
lineEdits.flag.each { lineEdits_Flag ->
editsMap[lineEdits_Flag.id] =
new Stuff(
flagMnemonic: lineEdits_Flag.mnemonic,
action: lineEdits_Flag.action)
}
and get it back out with
def myStuffFor10001 = editsMap['10001']
println myStuffFor10001.flagMnemonic // should equal 'TRA'
println myStuffFor10001.action // should equal 'review'
Also there's an easy alternative to using ?: "default" to set default values, you can use withDefault when creating your map:
def defaultStuff = new Stuff(
flagMnemonic: "defaultMnemonic", action:"defaultAction")
def editsMap = [:].withDefault { defaultStuff }
so that whenever you ask for something from the map that is not present there, you get the specified default object.
Why not use a list and closure like:
editsList = [
[FlagId:10001, FlagMnemonic:TRA, Action:review],
[FlagId:10002, FlagMnemonic:REB, Action:deny],
[FlagId:10003, FlagMnemonic:UNB, Action:deny],
[FlagId:20001, FlagMnemonic:REB, Action:deny],
[FlagId:20002, FlagMnemonic:ICD, Action:review],
[FlagId:30001, FlagMnemonic:REB, Action:deny],
[FlagId:40001, FlagMnemonic:ICD, Action:review],
[FlagId:40002, FlagMnemonic:MPR, Action:review],
[FlagId:50001, FlagMnemonic:CPT, Action:deny],
[FlagId:60001, FlagMnemonic:DTU, Action:deny],
[FlagId:70001, FlagMnemonic:ICD, Action:review],
[FlagId:70002, FlagMnemonic:MPR, Action:review]
]
def appliedEditsMap = {property,idValue->
return editsList.find{it[property] == idValue}
}
def thisValue = appliedEditsMap(FlagId, '10001') ?: "default"
You need to put this in to a class and then add that class in to the Map. From what I see your information is related so having a class to store makes sense unless I'm missing anything
What you can do is define your class as
class Flag {
String flagID
String flagMnemonic
String action
}
Now Put your Flag in to your map as
editsMap.put(10000,newFlag(flagID:'10000',flagMnemonic:'TES',action:'tes'))
I had a similar issue recently and I knew it was possible because some coworkers had done it. Reading the answers here and experimenting, I finally found a simple answer that worked for my use-case and isn't difficult to read. Writing a "generic code" answer makes this a little less readable than it is in our code with the proper column names, etc...
In my case, I was getting a List<Map> back from a repo query; I needed something like Map<String, List<Object>> and I need to add to the List if a result set's key matched a previous one. Of course, my Object wasn't a POJO, but you can use any Class. And to further complicate it, I needed to create a composite key from a few of the result values (don't ask, I didn't create it) and remove those keys from the original Map so I could use the remaining entries to create a business Object.
Here's what I did:
List<Map> listMap = repo.findWhateverItWas()
Map<String, List<Object>> resultMap = [:].withDefault {[]} //required to avoid NPE
listMap.each { Map<String, Object> it ->
String compKey = it['col1'] + it['col2'] + it['col3']
Map tempMap =[:]
it.keySet.each { k ->
if (!(k in ['col1','col2','col3'])) {
tempMap << [(k): it[k]] // this is the business Object result map
}
}
// createEntity is a static Entity Factory
// the simple += is the magic
resultMap[(compKey)] += createEntity(Entity.class, tempMap)
}
return resultMap
I realize this doesn't address your specific scenario, but I do believe it answers the question and provides an answer for a more complex situation.
I was able to prove the expected functionality of this with a simple test case. We use Spock...
def "Map of Lists test"() {
given:
Map<String, List<String>> map = [:].withDefault { [] }
when:
map['key1'] += 'item1'
map['key1'] += 'item2'
map['key2'] += 'item3'
map['key1'] += 'item4'
map['key2'] += 'item5'
then:
map['key1'] == ['item1', 'item2', 'item4']
map['key2'] == ['item3', 'item5']
}

How use a variable name to point different data types with the same name?

I have 2 List one stores the name of filterable columns(of type DropDown) and another store the values to load in those filterable columns.
List<string> filterableFields = new List<string>() { "A_B", "C_D", "E_F" };
List<string> AB, CD , EF;
Now at the run time I get the data from web service and I have written a function to to extract values for these filterable fields and store the values to 2nd List.
private void prepareListForFilterableColumns(XDocument records)
{
foreach (var currentField in filterableFields)
{
var values = (from xml in records.Descendants(z + "row")
let val = (string)xml.Attribute("ows_" + currentField.Replace("_", "_x0020_"))
where val != ""
orderby val
select val
).Distinct();
switch (currentField)
{
case "A_B": AB = values.ToList(); break;
case "C_D": CD = values.ToList(); break;
}
}
}
Now I was thinking that instead of hard coding the assignment in swtich case block, If I could just use the first List name "A_B" and replace "_" from it to point to my 2nd List and assign values.ToList() to it.
I understand that c# is a static language, So not sure if we can achieve this, but IF I can it will make my function generic.
Thanks a lot in advance for time and help.
Vishal
You could use a dictionary of lists of strings instead of 3 lists to store the values.
Dictionary<string, List<string>> val lists = new Dictionary<string,List<string>>();
And make the keys of the dictionary equal to the filterables: "AB", "CD",..
then, instead of AB you would use valLists["AB"] and could then reference reach list based on a string key.
The other option would be to use reflection but that would be slower and unnecessarily a bit more complicated.

Resources