Kotlin - creating a mutable list with repeating elements - 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.

Related

Split list of objects by delimiter in Kotlin

I have a List of objects I want to split by a delimiter into sublists, e.g:
val tokens = listOf(
Token(name = "lorem", val = "ipsum"),
Token(name = "dolor", val = "sit"),
Token(name = "newline", val = "\n"),
Token(name = "amet", val = "consectetur")
)
The delimiter should be any Token whose name is "newline", so after the split, tokens should become:
listOf(
listOf(
Token(name = "lorem", val = "ipsum"),
Token(name = "dolor", val = "sit")
),
listOf(
Token(name = "amet", val = "consectetur")
)
)
I've written my own function to do this already, but is there some elegant, built-in (preferably functional) way of doing it? I say this because I'm learning Kotlin and, coming from C++, find myself "reinventing the wheel" a lot with these types of things.
In such cases I suggest not going with too much functional transformations. We can for example do it by folding/reducing, we can also first find indices of all delimiters and then zipWithNext() between them to get ranges, etc. This way we get the solution in a very few lines of code, but this code will be very hard to read and understand.
Instead, I suggest going with a good old and very simple loop. However, to make it smoother and more performant, we can use sequences and subList():
fun main() {
val tokens = ...
tokens.splitBy { it.name == "newline" }
}
fun <T> List<T>.splitBy(predicate: (T) -> Boolean): Sequence<List<T>> = sequence {
if (isEmpty()) return#sequence
var last = 0
forEachIndexed { i, v ->
if (predicate(v)) {
yield(subList(last, i))
last = i + 1
}
}
yield(subList(last, size))
}
Please note this solution does not involve any data copying. It iteratively creates views of the original list, so it should be pretty fast. On the other hand, it should be used with care if the original list may change.
Also, you need to be aware of corner cases like: delimiter at the beginning and end, no delimiters in the list or empty list. There is no single answer to how splitting should work in these cases. Whatever solution you pick, I suggest checking it for these cases. My above solution mirrors the behavior of String.split().
I think there isn't any extension function in standard library for handling this case. You will have to write your own logic. You can do something like this:
val newList = mutableListOf<List<Token>>()
var subList = mutableListOf<Token>()
for (token in tokens) {
if (token.name == "newline") {
newList += subList
subList = mutableListOf()
} else {
subList += token
}
}
if (subList.isNotEmpty())
newList += subList
println(newList)
You can also extract this code out in the form of an extension function:
fun <T> List<T>.split(delimiter: (T) -> Boolean): List<List<T>> {
val newList = mutableListOf<List<T>>()
var subList = mutableListOf<T>()
for (token in this) {
if (delimiter(token)) {
newList += subList
subList = mutableListOf()
} else {
subList += token
}
}
if (subList.isNotEmpty())
newList += subList
return newList
}
// Usage
fun main() {
val tokens = listOf(
Token(name = "lorem", val = "ipsum"),
Token(name = "dolor", val = "sit"),
Token(name = "newline", val = "\n"),
Token(name = "amet", val = "consectetur")
)
println(tokens.split { it.name == "newline" })
}
You can use fold:
tokens
.fold(mutableListOf(mutableListOf<Token>())) { l, elem ->
if(elem.name=="newline") l.add(mutableListOf())
else l.last().add(elem)
l
}
The first parameter is the initial value, a list with a single list in it (if there isn't any newline, you still want to have a single list containing the elements).
The second parameter is a function that is executed for every element.
If the token name is newline, it adds a new list. If not, it adds the element to the last list.
The last line of fold containing l makes sure that the list is returned.

split list by delimiter in Kotlin

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]
)
}

How can I get the elements of list using ListBuffer

I am facing a very weird problem while getting elements of a list
Below is the piece of code where I am passing arguments as "bc" and "mn"
val list1 = List("abc", "def", "mnp")
val list2 = List(args(0), args(1))
val header1=list1.filter(x => list2.exists(y => x.contains(y)))
println(header1)
Output-List("abc","mnp")
I am trying to do it in a different way (by passing the same arguments)but getting an empty List
val list1 = List("abc", "def", "mnp")
//val list2 = List(args(0), args(1))
val ipList1= new ListBuffer[Any]
for(i <- 0 to 1){
ipList1 +=args(i)
}
val list2=ipList1.toList
println(list2)
val header1=list1.filter(x => list2.exists(y => x.contains(y)))
println(header1)
Output-List(bc, mn)
List()-->This is the empty List I am getting
Can Someone please tell where I am doing it wrong and How to make it right?
The problem is that x.contains(y) does not mean what you think it means. String has a contains method that checks whether another String is a substring of this String. But in your code y doesn't have type String, but type Any. So the contains method of String isn't called. It's the contains method of WrappedString which treats the String x as though it's a Seq[Char]. That method doesn't check whether any substring is equal to y but whether any character is equal to y.
The solution, obviously, is to use a ListBuffer[String].
The problem is that you are using a ListBuffer[Any] thus the elements lost their type information from String to Any and apparently that changes the semantics of the code.
You may either do this:
val ipList1 = new ListBuffer[String]
for (i <- 0 to 1) {
ipList1 += args(i).toString
}
val list2 = ipList1.toList
Or even better just:
val list2 = args.slice(0, 2).toList

Subtract two Lists in Kotlin using a custom equal function

I have two Lists and I want to get the List containing only the elements in the first list that are not in the second one. The problem is that I need to specify a custom equal when subtracting. Let's suppose I want to use one of the fields in the entries of the lists. Let's say the id.
I implemented it this way:
list1.filter { log -> list2.none { it.id == log.id } }
or
val projection = logEntries.map { it.id }
list1.filter { it.id !in projection }
Is there a better way to do it? Please take into account that I can't set a new equal method for the class.
The way you do it is ok, but when the lists become bigger you might want to do that:
You could make the process more efficient by turning your reference list (list2) to a set first.
val referenceIds = list2.distinctBy { it.id }.toSet()
list1.filterNot { it.id in referenceIds }
Background:
An ArrayList which you are most likely using has a time complexity of O(n) when you check if an element is contained. So, if the list gets bigger, it will take longer.
A HashSet on the other hand has a time complexity of O(1) when checking if an element is contained. So, if list2 becomes bigger it won't get slower.
Another approach?
fun main() {
val list1 = listOf(0, 1, 2, 3, 4, 5)
val list2 = listOf(2,3,4)
println(list1.filterNotIn(list2))
}
fun <T> Collection<T>.filterNotIn(collection: Collection<T>): Collection<T> {
val set = collection.toSet()
return filterNot { set.contains(it) }
}
Output: [0, 1, 5]
As per comments, there's no built-in way to do this.  (Probably not for any fundamental reason; just because no-one saw a need.)
However, you can easily add one yourself.  For example, here's your first suggestion, converted to extension function:
fun <T, R> Collection<T>.minus(elements: Collection<T>, selector: (T) -> R?)
= filter{ t -> elements.none{ selector(it) == selector(t) } }
You could then call this in the same way that a built-in function would work:
list1.minus(list2){ it.id }
(There are probably more efficient implementations, but this illustrates the idea.)
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/minus.html
fun main() {
val list1 = listOf(0, 1, 2, 3, 4, 5)
val list2 = listOf(2,3,4)
println(list1.minus(list2))
}

Scala access sequence of Maps

I have a IndexedSeq[Map[String, String]] and I would like to extract value where key is "text" and I would like to put it in a val text:IndexedSeq[String]. I have written the following piece but it doesn't work:
val text:IndexedSeq[String] = _
for(j <- 0 to indSeq.length-1){
text(j) = indSeq(j).get("text")
}
You are probably seeing a compiler error because indSeq(j).get("text") returns an Option[String], not a String.
If you just want to get all the values for the key "text" in a sequence, use:
val text = indSeq flatMap (_ get "text")
If it's important that the indices of both sequences line up, then you will want to substitute a default value in case the key "text" is not present:
val text = indSeq map (_.getOrElse("text", "default"))
I think the best approach is with a for-comprehension with a guard to get rid of the maps that don't have the "text" element:
val result = for {
i <- 0 until indexSeq.length
map = indexSeq(i)
if map isDefinedAt ("text")
} yield { (i, map("text")) }
val seq = result.toIndexedSeq
That way you keep the original indexes with the map. It also avoids holding any var value, which is always a perk
Since you were trying to use a for-comprehension originally, you might also be interested in doing it this way:
val text = (for { m <- indSeq } yield m get "text") flatten
EDIT
or if you want a default value you could do:
val text = for { m <- indSeq } yield m getOrElse("text", "default")