I have a below list
val list = listOf("o=one", "t=two", "t=two", "f=four", "o=one", "t=two", "s=seven", "o=one")
I wanna split it into list of the list contains [["o=one", "t=two", "t=two", "f=four"],["o=one", "t=two", "s=seven"],["o=one"]]
Actually I want to group list by "o=" delimiter and the list will always have at least one "0=" value. How could I achieve this in Kotlin without creating mutable var keyword because of my code should be in the functional style?
I have tried with group() and groupBy{} methods but couldn't get the expected result.
This might not cover all edge cases, and there might be better ways of doing it, but given that your requirements are not fully clear and extremely contrived, this should get you started either way. From here on out you can polish it yourself.
// Identifies indices of all "o=" elements in the list.
val splitAt = list
.withIndex()
.filter { it.value.startsWith( "o=" ) }
.map { it.index }
// Create sublists.
val split = splitAt
.windowed( 2, step = 1, partialWindows = true )
.map {
list.subList(
it[0],
if (it.count() == 1) it[0] + 1 else it[1]
)
}
The following code (written in Kotlin) extracts the elements from a list of lists. It works, but looks rather ugly and difficult to read.
Is there a nicer way to write the same with the java stream api? (Examples can be given in Kotlin or Java)
val listOfLists: List<Any> = ...
val outList: MutableList<Any> = mutableListOf()
listOfLists.forEach {
list ->
if (list is ArrayList<*>) list.forEach {
l ->
outList.add(l)
}
}
return outList;
In Kotlin it's super easy without any excessive boilerplate:
val listOfLists: List<List<String>> = listOf()
val flattened: List<String> = listOfLists.flatten()
flatten() is the same as doing flatMap { it }
In Java, you need to utilize Stream API:
List<String> flattened = listOfLists.stream()
.flatMap(List::stream)
.collect(Collectors.toList());
You can also use Stream API in Kotlin:
val flattened: List<String> = listOfLists.stream()
.flatMap { it.stream() }
.collect(Collectors.toList())
You could flatMap each list:
List<List<Object>> listOfLists = ...;
List<Object> flatList =
listOfLists.stream().flatMap(List::stream).collect(Collectors.toList());
Use flatMap
List<Integer> extractedList = listOfLists.stream().flatMap(Collection::stream).collect(Collectors.toList());
flatMap() creates a stream out of each list in listOfLists. collect() collects the stream elements into a list.
What would be an idiomatic way to create a mutable list of a given length n with repeating elements of value v (e.g listOf(4,4,4,4,4)) as an expression.
I'm doing val list = listOf((0..n-1)).flatten().map{v} but it can only create an immutable list.
Use:
val list = MutableList(n) {index -> v}
or, since index is unused, you could omit it:
val list = MutableList(n) { v }
another way may be:
val list = generateSequence { v }.take(4).toMutableList()
This style is compatible with both MutableList and (Read Only) List
If you want different objects you can use repeat.
For example,
val list = mutableListOf<String>().apply {
repeat(2){ this.add(element = "YourObject($it)") }
}
Replace String with your object. Replace 2 with number of elements you want.
You're able to use the ranges for this purpose, e.g.
val listOfFour = (1..10).map { 4 }
or
val objectList = (1..10).map {
YourClass(
arg1 = "someValue",
arg2 = it
)
}
if you need you can use it (index) for your needs as well.
How can I convert a map to a list without the map keys being involved in the list in scala?
val mp = collection.mutable.Map[Long, String]()
mp(0) = "val0"
mp(1) = "val1"
mp(2) = "val2"
mp.toList //I want: List("val0", "val1", "val2")
You are probably looking for
mp.values.toList
I've struggled to find a way to stay true to functional style in for expressions when I need to collect multiple parameters of an object into a List.
As an example, say I have a Notification object, which has both a fromId (the user id the notification is from) and an objectOwnerId (the id of the user who created the original object). These can differ in facebook style notifications ("X also commented on Y's post").
I can collect the userIds with a for expression like so
val userIds = for { notification <- notifications } yield notification.fromId
however say I want to collect both the fromIds and the objectOwnerIds into a single list, is there any way to do this in a single for expression without the user of vars?
I've done something like this in the past:
var ids = List()
for {
notification <- notifications
ids = ids ++ List(notification.fromId, notification.objectOwnerId)
}
ids = ids.distinct
but it feels like there must be a better way. The use of a var, and the need to call distinct after I complete the collection are both ugly. I could avoid the distinct with some conditionals, but I'm trying to learn the proper functional methods to do things.
Thanks in advance for any help!
For such cases, there is foldLeft:
(notifications foldLeft Set.empty[Id]) { (set, notification) =>
set ++ Seq(notification.fromId, notification.ownerId)
}
or in short form:
(Set.empty[Id] /: notifications) { (set, notification) =>
set ++ Seq(notification.fromId, notification.ownerId)
}
A set doesn't hold duplicates. After the fold you can convert the set to another collection if you want.
val userIds = for {
notification <- notifications
id <- List(notification.fromId, notification.objectOwnerId)
} yield id
Apply distinct afterwards if required. If the id can only be duplicated on a single notification, you can apply distinct on the second generator instead.
Sure, instead of just yielding the fromId, yield a tuple
val idPairs:List[(String, String)] = for(notification <- notifications) yield(notification.fromId, notification.objectOwnerId)
Well, here is my answer to the following:
How to map from [Y(x1, x2), Y(x3, x4)] to [x1,x2,x3,x4]?
Use flatMap (see Collection.Traversable, but note it's actually first defined higher up).
case class Y(a: Int, b: Int)
var in = List(Y(1,2), Y(3,4))
var out = in.flatMap(x => List(x.a, x.b))
> defined class Y
> in: List[Y] = List(Y(1,2), Y(3,4))
> out: List[Int] = List(1, 2, 3, 4)
Also, since for..yield is filter, map and flatMap in one (but also see "sugar for flatMap?" that points out that this isn't as efficient as it could be: there is an extra map):
var out = for { i <- in; x <- Seq(i.a, i.b) } yield x
I would likely pick one of the other answers, however, as this does not directly address the final problem being solved.
Happy coding.
You can also use Stream to transform the pairs into a stream of individual items:
def toStream(xs: Iterable[Y]): Stream[Int] = {
xs match {
case Y(a, b) :: t => a #:: b #:: toStream(t)
case _ => Stream.empty
}
}
But like pst said, this doesn't solve your final problem of getting the distinct values, but once you have the stream it's trivial:
val result = toStream(ys).toList.removeDuplicates
Or a slight modification to the earlier suggestions to use flatten - add a function that turns a Y into a List:
def yToList(y: Y) = List(y.a, y.b)
Then you can do:
val ys = List(Y(1, 2), Y(3, 4))
(ys map yToList flatten).removeDuplicates
I agree with Dave's solution but another approach is to fold over the list, producing your map of id to User object as you go. The function to apply In the fold will query the db for both users and add them to the map being accumulated.
What about simple map? AFAIK for yield gets converted to series of flatMap and map anyway. Your problem could be solved simply as follows:
notifications.map(n => (n.fromId, n.objectOwnerId)).distinct