Reputation: 2392
I see that #filter
is defined on Map
, but I am not able to figure out how to use it. Anyone care to share an example?
I have a deeply nested TreeMap instance (TreeMap<String, Map<String, Map<*, *>>>
) and I want to filter/find
the first (which is the only in the domain) top-level key that has a certain characteristics associated with something deeper in the value.
Here's how the data looks like:
{
"i1": {
"aliases": {}
},
"i2": {
"aliases": {}
},
"i3": {
"aliases": {}
},
"i4": {
"aliases": {
"alias-im-looking-for": {}
}
}
}
I have this following non-functional code which solves it right now:
val indexToAliasMappingType = LinkedTreeMap<String, Map<String, Map<*, *>>>()
val indexToAliasMappings = Gson().fromJson(response.jsonString, indexToAliasMappingType.javaClass)
var currentIndexName = ""
for ((index, aliasMappings) in indexToAliasMappings) {
val hasCurrentAlias = aliasMappings.get("aliases")?.containsKey(alias)
if (hasCurrentAlias != null && hasCurrentAlias) {
currentIndexName = index
}
}
return currentIndexName
Upvotes: 11
Views: 12880
Reputation: 23115
You cannot invoke firstOrNull
on the map itself, but you can invoke it on its entries
set:
val currentIndexName = indexToAliasMappings.entries.firstOrNull {
it.value["aliases"]?.containsKey(alias) == true
}?.key
firstOrNull
iterates through map entries and stops on a first entry matching the predicate. No intermediate map allocations as in the variant with filter
or filterValues
are required here.
Upvotes: 5
Reputation: 728
Looks like what you need is the key part of the first entry that matches your criteria. Something like this (single line intentional) should help:
val currentIndexName = indexToAliasMappings.filterValues { it["aliases"]?.containsKey(alias) ?: false }.keys.firstOrNull() ?: ""
Upvotes: 6
Reputation: 595
.filter
's lambda returns a boolean for each entry in the map. If that boolean evaluates to true
, that entry is part of the returned collection. If that boolean evaluates to false
, that entry is filtered out.
val indexToAliasMapping = linkedMapOf(
"i1" to mapOf(
"aliases" to mapOf<Any, Any>()
),
"i2" to mapOf(
"aliases" to mapOf<Any, Any>()
),
"i3" to mapOf(
"aliases" to mapOf<Any, Any>()
),
"i4" to mapOf(
"aliases" to mapOf(
"aliases-im-looking-for" to "my-alias"
)
)
)
// If you only need one, you could also use .first instead of .filter, which
// will give you a Pair<String, Map<String, Map<*, *>>> instead of a List
val allWithNonEmptyAliases: Map<String, Map<String, Map<*, *>>> = indexToAliasMapping.filter {
it.value["aliases"]?.containsKey(alias) ?: false
}
// We use .toList() so you can get the first element (Map doesn't allow you to retrieve by index)
return allWithNonEmptyAliases.toList().first().first
Upvotes: 2