i have a list in my project like
val list1 = listOf("1", "pig", "3", "cow")
and i need to swap it to like "pig", "1", "cow", "3"
and the number and words will be random so it cant be only on these words
can anyone tell me how to do this?
You can combine chunked() to get pairs of items and then flatMap() to swap them and recreate a flat list:
list1
.chunked(2)
.flatMap { listOf(it[1], it[0]) } // or: it.reversed()
However, it looks pretty weird that you have a list like this in the first place. If items of this list are stored in two subsequent indexes, then such design complicates maintaining and processing of the data. Instead, create a data class for both fields and create a list of such data items:
val list = listOf(Animal("1", "pig"), Animal("3", "cow"))
data class Animal(
val id: String,
val name: String,
)
Related
I have a map<String, MyObject> where the values in the map could have same values for some variables (e.g., name parameter in my example). I would appreciate any solution using streams to remove entries with same name parameter on the value and keep only one of them with minimum id.
data class MyObject(val id: Int, val name: String)
For instance my map could be:
[
"first" to MyObject(1, "Alice"),
"second" to MyObject(2, "Bob"),
"third" to MyObject(3, "Alice")
]
and the expected output is:
[
"first" to MyObject(1, "Alice"),
"second" to MyObject(2, "Bob")
]
where the entry with key third is removed because the value has the same name as the first entry.
First, we need to identify all of the duplicate candidates. We can do that with groupBy, which works on any iterable (and Map is iterable with iteratee type Entry<K, V>).
myMap.entries
.groupBy({ entry => entry.value.name })
This produces a value of type Map<String, List<Entry<String, MyObject>>>.
Now, for each value in the map, we want to choose the element in the list with the smallest ID. We can select the minimum element by some condition using minBy and can do that to each element of a map with mapValues.
myMap.entries
.groupBy({ entry => entry.value.name })
.mapValues({ entry => entry.value.minBy({ it.value.id })!! })
(Note: groupBy always produces nonempty lists, since it's partitioning a set, so we can confidently !! assert that a minimum exists)
Finally, this returns a Map<String, Entry<String, MyObject>>, and you probably want to eliminate the excess Map layer.
myMap.entries
.groupBy({ entry -> entry.value.name })
.mapValues({ entry -> entry.value.minBy({ it.value.id })!! })
.values
.associate({ it.key to it.value })
Try it online!
There are multiple ways to do this using pure Kotlin, here is one relying on the fact that hash-maps do not allow duplicates. I am sure there are better solutions out there:
values.toList()
// If you care about the smaller id number value
// then sort by descending so they replace larger values.
.sortedByDescending { it.second.id }
// Will replace duplicates by hashing technique
.associateBy { it.second.name }
// Back to the same data structure
.map { it.value.first to it.value.second }.toMap()
Try it online!
I am looking for a way to neatly filter the values from the event and create a map based on them.
What we need to know:
Event:
The event contains the dictionary data structure (its a map of <String, String> where key is a product parameter name.
For example:
a)
Key: Brand,
Value: Nike
b)
Key: Producer,
Value: Audi
Filter:
Filter is a Map<String, Set<String>> and its configured in yml.
Example of values:
Key: Manufacturer,
Value: [Brand, Producer, Creator, Author]
Entity:
data class Product(
val id: String,
val parameters: Map<String, Parameter> = emptyMap()
)
data class Parameter(
val parameterId: String?,
val parameterValue: String)
How flow looks like:
1)An event comes with a list of parameters, e.g.
mapOf(
"brand" to "Nike",
"size" to "42",
"color" to "black
)
Values are filtered. Lets pretend we have got only one filter - its key = Manufacturer, its values = [Brand, Producer, Creator, Author].
So from this event we have got only one position - its mapOf("brand" to "Nike")
During filtering, a map is created in the domain object [Product] with filled field- parameters: Map <String, Parameter>
e.g.
mapOf("Manufacturer" to Parameter(null, "Nike"))
As you can see - Manufacturer, not Brand, was selected as the key in the map in the Product object (i.e. the key of this parameter, not the value).
GOAL:
What should the method of filtering the event look like?How to do it in clever, Kotlin way (filter the data using the Map <String, Set > for the incoming map and create the target object based on it.)
Thanks in advance for help/advice!
EDIT:
More information:
Sample input:
Incoming event that appears on the domain side:
ProductCreatedEvent( id = "1234", mapOf("brand" to "Nike", "color" to "black, "size" to "46"))
Sample output:
Product(id = "1234", mapOf("Manufacturer" to "Nike"))
The incoming event was filtered by a filter with the structure Map <String, Set <String>>
The structure of the filter is dictated by the fact that the data comes from various sources and thus, for example, the "manufacturer" key on the supplier's side may be called "brand" or "producer". On the side of my service it is to be standardized and therefore the key value per key is selected in the parameter map in the domain object.
This should work
val event = ProductCreatedEvent( id = "1234", mapOf("Brand" to "Nike", "Color" to "Black", "Size" to "46"))
val filters = mapOf("Manufacturer" to setOf("Brand", "Producer", "Creator", "Author"))
val parameters = event.params.mapNotNull { param ->
filters.entries.find { it.value.contains(param.key) }
?.let { it.key to param.value }
}.toMap()
val product = Product(event.id, parameters)
Introduction
Let's say I have a Kotlin enum class:
enum class Type {
ONE,
TWO,
THREE,
FOUR
}
and a following data class:
data class Item(
val name: String,
val type: Type
)
Then I have a Single that emits a list of Items – can by anything but for example purposes, let's say it looks like that:
val itemsSingle = Single.just(listOf(
Item("A", Type.ONE),
Item("B", Type.ONE),
Item("C", Type.TWO),
Item("D", Type.THREE),
))
Problem
What I'd like to achieve is to have an RxJava stream that will output a map where keys come from Type and values are lists of Items matching a given Type value (where an undetermined, unsorted list of Items is provided by some other Single stream). The signature would be:
Single<Map<Type, List<Item>> // or Observable<Map<Type, List<Item>>
One additional requirement is that the map's keys should always exhaust all values from Type enum even if the itemsSingle stream contains no items for some Type values (or no items at all). So, for the provided itemsSingle example stream the returned map should look like this:
{
ONE: [ Item(name: "A", type: ONE), Item(name: "B", type: ONE) ],
TWO: [ Item(name: "C", type: TWO) ],
THREE: [ Item(name: "D", type: THREE) ],
FOUR: []
}
Attempt
With all the above, I've kinda achieved the desired result with following steps:
To satisfy the requirement of exhausting all Type enum values I first create a map that has an empty list for all possible Type values:
val typeValuesMap = Type.values().associate { it to emptyList<Item>() }
val typeValuesMapSingle = Single.just(typeValuesMap)
// result: {ONE=[], TWO=[], THREE=[], FOUR=[]}
I can get a map that contains items from itemsSingle grouped under respective Type value keys:
val groupedItemsMapSingle = itemsSingle.flattenAsObservable { it }
.groupBy { it.type }
.flatMapSingle { it.toList() }
.toMap { list -> list[0].type } // the list is guaranteed to have at least one item
// result: {ONE=[Item(name=A, type=ONE), Item(name=B, type=ONE)], THREE=[Item(name=D, type=THREE)], TWO=[Item(name=C, type=TWO)]}
finally I can combine both lists using the combineLatest operator and overwriting initial empty list of items for a given Type value if itemsSingle contained any Items for this Type value:
Observable.combineLatest(
typeValuesMapSingle.flattenAsObservable { it.entries },
groupedItemsMapSingle.flattenAsObservable { it.entries }
) { a, b -> listOf(a, b) }
.defaultIfEmpty(typeValuesMap.entries.toList()) // in case itemsSingle is empty
.flatMapIterable { it }
.collect({mutableMapOf<Type, List<Item>>()}, { a, b -> a[b.key] = b.value})
// result: {FOUR=[], ONE=[Item(name=A, type=ONE), Item(name=B, type=ONE)], THREE=[Item(name=D, type=THREE)], TWO=[Item(name=C, type=TWO)]}
Summary
As you can see, it's quite a lot of code for a seemingly simple operation. So my question is – is there a simpler way to achieve the result I'm after?
Just merge a map of empty lists with a map of filled lists
val result = itemsSingle.map { items->
Type.values().associateWith { listOf<Item>() } + items.groupBy { it.type }
}
This question already has answers here:
How to split a list into sublists using a predicate with Kotlin?
(2 answers)
Closed 1 year ago.
I want to group specific items in a list based on a 'split predicate'. An example of what I mean:
val list = listOf("1", "2", "3", "", "4", "5", "6", "", "7")
// looking for: a nice readable (maybe functional?) way to get to:
val result = listOf(
listOf("1", "2", "3"),
listOf("4", "5", "6"),
listOf("7"),
)
As you see, I want to split the list on items which are item.isBlank() - which I also want to drop in the process.
I know how to do it imperatively, but I think there must be a nice, readable, functional way!
Thanks a lot & regards
Marc
I had a solution that is similar to Matt's (had to run to a meeting and couldn't post it earlier!). It is materially the same, but allows you to specify an arbitrary predicate to split on, accounts for the fact that you might start with a blank item and end up with an unwanted empty list (or if you have two items that match the predicate in a row), and is defined as an extension function:
fun <T> List<T>.split(predicate: (T) -> Boolean): List<List<T>> =
fold(mutableListOf(mutableListOf<T>())) { acc, t ->
if (predicate(t)) acc.add(mutableListOf())
else acc.last().add(t)
acc
}.filterNot { it.isEmpty() }
And to call it:
list.split { it.isBlank() }
// Returns [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
There's probably a cleaner way but I think this solves it
list.fold (mutableListOf(mutableListOf<String>())) { acc, item ->
if (item.isBlank()) {
acc.add(mutableListOf())
} else {
acc.last().add(item)
}
acc
}
I have an object that looks like this:
data class Product(val name: String,
val maker: List<String>)
Currently, the response that I receive from the backend (and it can't be changed) is as follows:
[{"name":"Car", "maker":["Audi"]},
{"name":"Car", "maker":["BMW"]},
{"name":"Motorcycle", "maker":["Yamaha"]},
{"name":"Motorcycle", "maker":["Kawasaki"]}
]
The actual list consists of a lot of data, but the name field can be trusted to be grouped by.
What would be a way for me to map this data so that the end result is something like this:
[{"name":"Car", "maker":["Audi", "BMW"]},
{"name":"Motorcycle", "maker":["Yamaha","Kawasaki"]}
]
Just use groupBy { ... } and then process the groups map entries, replacing them with a single Product:
val result = products.groupBy { it.name }.entries.map { (name, group) ->
Product(name, group.flatMap { it.maker })
}