Is there any way to check a HashMap if it contains a certain set of keys which keys are given in an array. When I try something like the code below it returns false.
map.containsKey(arrayOf("2018-01-16"))
If I try the following code it works but I need to check for the keys and the numbers of keys that I need to search is not fixed.
map.containsKey("2018-01-16")
You can start from the keys themselves, and use the all function from the standard library:
val map = hashMapOf(...)
val keys = arrayOf("2018-01-16", "2018-01-17", "2018-01-18")
val containsAllKeys = keys.all { map.containsKey(it) }
If you do this a lot and want to have this functionality on the Map type, you can always add it as an extension:
fun <K, V> Map<K, V>.containsKeys(keys: Array<K>) = keys.all { this.containsKey(it) }
val containsAllKeys = map.containsKeys(arrayOf("2018-01-16", "2018-01-17"))
You might also want to overload the extension with another function that takes an Iterable<K> as the parameter.
Map has keys collection, which as every collection implements containsAll method, so you can use it to check whether the keys collection contains all of the keys:
map.keys.containsAll(keysArray.asList())
You could use ArrayList<T> as a key, since it's equals is different as the Array<T> one. Let's see this test:
class ExampleUnitTest {
#Test
fun arrayAsKeyTest() {
val hashMapOne = HashMap<Array<String>, Int>()
val stringKeysOne1 = arrayOf("a", "b")
hashMapOne.set(stringKeysOne1, 2)
val stringKeysOne2 = arrayOf("a", "b")
// NOT MATCH! As stringKeysOne1 == stringKeysOne2 is false
assertFalse(hashMapOne.containsKey(stringKeysOne2)) // NOT MATCH
val hashMapTwo = HashMap<ArrayList<String>, Int>()
val stringKeysTwo1 = arrayListOf("a", "b")
hashMapTwo.set(stringKeysTwo1, 2)
val stringKeysTwo2 = arrayListOf("a", "b")
// MATCH! As stringKeysTwo1 == stringKeysTwo2 is true (although stringKeysTwo1 === stringKeysTwo2 is false)
assertTrue(hashMapTwo.containsKey(stringKeysTwo2)) // MATCH
}
}
Related
when ever I build my project I got this error
here is the kotlin class code
var textBitmap: Bitmap? = null
dynamicItem.dynamicText[imageKey]?.let { drawingText ->
dynamicItem.dynamicTextPaint[imageKey]?.let { drawingTextPaint ->
drawTextCache[imageKey]?.let {
textBitmap = it
} ?: kotlin.run {
textBitmap = Bitmap.createBitmap(drawingBitmap.width, drawingBitmap.height, Bitmap.Config.ARGB_8888)
val drawRect = Rect(0, 0, drawingBitmap.width, drawingBitmap.height)
val textCanvas = Canvas(textBitmap)
drawingTextPaint.isAntiAlias = true
val fontMetrics = drawingTextPaint.getFontMetrics();
val top = fontMetrics.top
val bottom = fontMetrics.bottom
val baseLineY = drawRect.centerY() - top / 2 - bottom / 2
textCanvas.drawText(drawingText, drawRect.centerX().toFloat(), baseLineY, drawingTextPaint);
drawTextCache.put(imageKey, textBitmap as Bitmap)
}
I couldn't figure out how to fix it
Instead of doing nested let like that, i would prefer to do some guard clause
val drawingText = dynamicItem.dynamicText[imageKey] ?: return // or you could assign an empty string `?: "" `
val drawingTextPaint = dynamicItem.dynamicTextPaint[imageKey] ?: return
val textBitmap: Bitmap = drawTextCache[imageKey] ?: Bitmap.createBitmap(drawingBitmap.width, drawingBitmap.height, Bitmap.Config.ARGB_8888).applyCanvas {
val drawRect = Rect(0, 0, drawingBitmap.width, drawingBitmap.height)
val fontMetrics = drawingTextPaint.getFontMetrics()
val top = fontMetrics.top
val bottom = fontMetrics.bottom
val baseLineY = drawRect.centerY() - top / 2 - bottom / 2
drawingTextPaint.isAntiAlias = true
drawText(drawingText, drawRect.centerX().toFloat(), baseLineY, drawingTextPaint);
}
drawTextCache.put(imageKey, textBitmap)
Basically Kotlin can't smart cast textBitmap to a non-null Bitmap inside that lambda. You're probably getting the error on the Canvas(textBitmap) call, which can't take a null parameter, and the compiler can't guarantee textBitmap isn't null at that moment.
It's a limitation of lambdas referencing external vars which can be changed - I think it's because a lambda could potentially be run at some other time, so no guarantees can be made about what's happening to that external variable and whether something else could have modified it. I don't know the details, there's some chat here if you like.
The fix is pretty easy though, if all you're doing is creating a textBitmap variable and assigning something to it:
// Assign it as a result of the expression - no need to create a var first and keep
// changing the value, no need for a temporary null value, it can just be a val
val textBitmap: Bitmap? =
dynamicItem.dynamicText[imageKey]?.let { drawingText ->
dynamicItem.dynamicTextPaint[imageKey]?.let { drawingTextPaint ->
drawTextCache[imageKey]
?: Bitmap.createBitmap(drawingBitmap.width, drawingBitmap.height, Bitmap.Config.ARGB_8888).apply {
val drawRect = Rect(0, 0, drawingBitmap.width, drawingBitmap.height)
val textCanvas = Canvas(this)
drawingTextPaint.isAntiAlias = true
val fontMetrics = drawingTextPaint.getFontMetrics();
val top = fontMetrics.top
val bottom = fontMetrics.bottom
val baseLineY = drawRect.centerY() - top / 2 - bottom / 2
textCanvas.drawText(drawingText, drawRect.centerX().toFloat(), baseLineY, drawingTextPaint);
drawTextCache.put(imageKey, this)
}
}
}
I'd recommend breaking the bitmap creation part out into its own function for readability, and personally I'd avoid the nested lets (because it's not immediately obvious what you get in what situation) but that's a style choice
Instead of using ifs and loops I would like to use sumBy function and give it a statement. Where to add condition?
val counter = list.sumBy {it.amount}
where amount is field from the list.
Where to add for example if(it.flag == true) statement?
Or just use streams?
val counter = list.sumBy { if (it.flag) it.amount else 0 }
or
val counter = list.asSequence().filter { it.flag }.sumBy { it.amount }
asSequence() for using sequence to prevent creataion an intermediate collection in filter function
If you have an array by name "amount" the list then items sum will we done by this method.
val amount= listOf(10, 20, 30)
println(amount.sumBy { it })
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]
I'm new to Scala and Slick, and I found this built-in type mapper from AnyType to Boolean. I need to map "y" to true and "n" to false, which seems to be simple. Can someone show me how to use it? My current code is like this, re-inventing the wheel:
sealed trait YesNoBoolean
case object True extends YesNoBoolean
case object False extends YesNoBoolean
implicit val boolTypeMapper = MappedColumnType.base[YesNoBoolean, String](
{ b =>
assertNotNull(b)
if(b == True) "y" else "n"
}, { i =>
assertNotNull(i)
if(i == "y") True else False
}
)
I actually need to map to the real Boolean type. I have several columns that use this nefarious scheme (legacy from PHP).
I think I need to do this in the Table definition:
object Record extends Table[(Int,YesNoBoolean)]("record") {
def is_usable_entry = column[YesNoBoolean]("usable_entry", O.DBType("enum('y','n')"))
...
}
Try this
def is_usable_entry = column[Boolean]
("usable_entry",O.DBType("enum('y','n')"))(
MappedColumnType.base[Boolean, String](
if(_) "y" else "n",_=="y"
)
)
If this does not work you may have to pull out the type mapper and make it implicit. In that case you probably have to exclude the other implicit type mapper for Boolean from your import (booleanColumnType => _).
I have got a List of strings like:
String1
String1.String2
String1.String2.String3
Other1
Other1.Other2
Test1
Stuff1.Stuff1
Text1.Text2.Text3
Folder1.Folder2.FolderA
Folder1.Folder2.FolderB
Folder1.Folder2.FolderB.FolderC
Now I would like to group this into:
String1.String2.String3
Other1.Other2
Test1
Stuff1.Stuff1
Text1.Text2.Text3
Folder1.Folder2.FolderA
Folder1.Folder2.FolderB.FolderC
If
"String1" is in the next item "String1.String2" I will ignore the first one
and if the second item is in the third I will only take the third "String1.String2.String3"
and so on (n items). The string is structured like a node/path and could be split by a dot.
As you can see for the Folder example Folder2 has got two different Subfolder items so I would need both strings.
Do you know how to handle this with Linq? I would prefer VB.Net but C# is also ok.
Regards Athu
Dim r = input.Where(Function(e, i) i = input.Count - 1 OrElse Not input(i + 1).StartsWith(e + ".")).ToList()
Condition within Where method checks if element is last from input or is not followed by element, that contains current one.
That solution uses the fact, that input is List(Of String), so Count and input(i+1) are available on O(1) time.
LINQ isn't really the correct approach here, because you need to access more than one item at a time.
I would go with something like this:
public static IEnumerable<string> Filter(this IEnumerable<string> source)
{
string previous = null;
foreach(var current in source)
{
if(previous != null && !current.Contains(previous))
yield return previous;
previous = current;
}
yield return previous;
}
Usage:
var result = strings.Filter();
Pretty simple one. Try this:
var lst = new List<string> { /*...*/ };
var sorted =
from item in lst
where lst.Last() == item || !lst[lst.IndexOf(item) + 1].Contains(item)
select item;
the following simple line can do the trick, I'm not sure about the performance cost through
List<string> someStuff = new List<string>();
//Code to the strings here, code not added for brewity
IEnumerable<string> result = someStuff.Where(s => someStuff.Count(x => x.StartsWith(s)) == 1);