I have input data of type List<UnitWithComponents>
class UnitWithComponents {
var unit: Unit? = null
var components: List<Component> = ArrayList()
}
I want to convert the data to a vararg of Unit
Currently I am doing *data.map { it.unit!! }.toTypedArray(). Is there a better way to do it?
fun foo(vararg strings: String) { /*...*/ }
Using
foo(strings = arrayOf("a", "b", "c"))
val list: MutableList<String> = listOf("a", "b", "c") as MutableList<String>
foo(strings = list.map { it }.toTypedArray())
Named arguments are not allowed for non-Kotlin functions (*.java)
So, in this case you should replace:
From: strings = list.map { it }.toTypedArray()
To: *list.map { it }.toTypedArray()
GL
Source
No, that's the correct way to do it (assuming you want to throw an exception when it.unit is null for some element of the list).
Related
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.
I have list of myObjects that contains duplicate values. I need to filter myObjects based on the latest version as shown in the below pic.
Please share the code either in kotlin (most helpful) or java. Thanks in advance.
Use groupingBy and reduce
Kotlin playground
data class MyObject(
val name: String,
val code: String,
val version: Int
)
fun main() {
val objects = listOf(
MyObject("Book1", "code_A", 2),
MyObject("Book1", "code_A", 1),
MyObject("Book2", "code_B", 3),
MyObject("Book2", "code_B", 2),
MyObject("Book2", "code_B", 1),
MyObject("Book3", "code_C", 1),
)
val result = objects
.groupingBy { it.code }
.reduce { _, a, b -> maxOf(a, b, compareBy { it.version }) }
.values
print(result.joinToString(separator = "\n"))
}
I'd like to know how to name objects in a list by a field
case class age(id: Int,name:String,age:Int)
val people: List[age] = List(age(2,"Angela",31),age(3,"Rick",28))
In this minimum example, I'd like to create objects Angela and Rick.
My initial idea:
val objects: List[age] = people.map( x => {val x.name = new age(x.name,x.age) })
But of course val x.name doesn't work because u can't use a variable name in a variable name.
This isn't an actual problem on a project but rather a concept I am stuck on.
It's not clear what's your intent. Do you want to create variables named angela and rick? You can do it manually for small number of list element and for large number of list element this doesn't make sense, because how would you use your 100 variables?
It seems you are talking about some mapping from names to properties and then Map will probably suit you the best
val peopleMap: Map[String,age] = people.map(p => p.name -> p). // this will create list of pairs
toMap // this will turn it to a Map
pritnln(peopleMap("Angela")) // now you can use person name to get all the info about them
A simple solution is to use a map:
case class Person(id: Int, name: String, age: Int)
val people: List[Person] = List(Person(2, "Angela", 31), Person(3, "Rick", 28))
val peopleByName: Map[String, Person] = people // List[Person]
.map(p => (p.name, p)) // List[(String, Person)]
.toMap // Map[String, Person]
or, starting with a Map instead of a List:
case class Person(id: Int, age: Int)
val peopleByName: Map[String, Person] = Map(
"Angela" -> Person(2, 31), // (String, Person)
"Rick" -> Person(3, 28) // (String, Person)
) // Map[String, Person]
However, if you want to define a member at runtime, then you'll have to extend the Dynamic trait (code snippet from here, the import is mine (required, otherwise the compiler won't be happy)):
import scala.language.dynamics
class DynImpl extends Dynamic {
def selectDynamic(name: String) = name
}
scala> val d = new DynImpl
d: DynImpl = DynImpl#6040af64
scala> d.foo
res37: String = foo
scala> d.bar
res38: String = bar
scala> d.selectDynamic("foo")
res54: String = foo
If you really want to do this, then I suggest this implementation:
class DynamicPeople(peopleByName: Map[String, Person]) extends Dynamic {
def selectDynamic(name: String) = peopleByName(name)
}
split() in swift 3 is deprecated. What will be alternate for below code:
var fullNameArr = split(str) {$0 == "#"}
let world = "Hello, world!".characters.suffix(6).dropLast()
String(world) // → "world"
Here split, which returns an array of subsequences, is also used for string processing. It’s defined like that:
extension Collection {
func split(maxSplits: Int = default,
omittingEmptySubsequences: Bool = default,
whereSeparator isSeparator: (Self.Iterator.Element) throws -> Bool) rethrows
-> [AnySequence<Self.Iterator.Element>]
}
For example:
let commaSeparatedArray = "a,b,c".characters.split { $0 == "," }
commaSeparatedArray.map(String.init) // → ["a", "b", "c"]
For more detail in split in swift 3
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")