Suppose I have the below lists and data classes in Kotlin:
val qrtr = listOf("Q1", "Q2", "Q3", "Q4")
data class X(val name: String, val location: String)
val x1 = X("John Doe", "USA")
val x2 = X("Jane Doe", "Singapore")
val allX = listOf(x1, x2)
data class Y(val title: String, val rating: Double)
val y1 = Y("Senior", 4.8)
val y2 = Y("Junior", 4.5)
val allY = listOf(y1, y2)
Is there an easy way to create another list using a 3rd data class using the above lists of allX, allY and qrtr.
data class Z(val x: X, val y: Y, quarter: String)
For the above data class (Z), I would like to merge details of allX and allY by its index position..and have this data repeated for each element in qrtr.
If it were to be done manually, it would look like this: But I'd like it done programmatically.
val z1 = Z(x1, y1, "Q1")
val z2 = Z(x1, y1, "Q2")
val z3 = Z(x1, y1, "Q3")
val z4 = Z(x1, y1, "Q4")
val z5 = Z(x2, y2, "Q1")
val z6 = Z(x2, y2, "Q2")
val z7 = Z(x2, y2, "Q3")
val z8 = Z(x2, y2, "Q4")
val allZ = listOf(z1, z2, z3, z4, z5, z6, z7, z8)
val result = allX.zip(allY).flatMap { (x, y) -> qrtr.map { q -> Z(x, y, q) } }
You can do this:
val allZ = allX.flatMapIndexed { index, x -> qrtr.map { q -> Z(x, allY[index], q) } }
allZ.forEach {
println(it)
}
Output:
Z(x=X(name=John Doe, location=USA), y=Y(title=Senior, rating=4.8), quarter=Q1)
Z(x=X(name=John Doe, location=USA), y=Y(title=Senior, rating=4.8), quarter=Q2)
Z(x=X(name=John Doe, location=USA), y=Y(title=Senior, rating=4.8), quarter=Q3)
Z(x=X(name=John Doe, location=USA), y=Y(title=Senior, rating=4.8), quarter=Q4)
Z(x=X(name=Jane Doe, location=Singapore), y=Y(title=Junior, rating=4.5), quarter=Q1)
Z(x=X(name=Jane Doe, location=Singapore), y=Y(title=Junior, rating=4.5), quarter=Q2)
Z(x=X(name=Jane Doe, location=Singapore), y=Y(title=Junior, rating=4.5), quarter=Q3)
Z(x=X(name=Jane Doe, location=Singapore), y=Y(title=Junior, rating=4.5), quarter=Q4)
Note that it will crash though if allX has more elements than allY
Related
Is there a simple way to divide list of Double into two lists of pairs in Kotlin?
In such way:
[x1, y1, x2, y2, x3, y3] => [(x1, x2), (x2, x3), (x3, x1)], [(y1, y2), (y2, y3), (y3, y1)]
I tried to use filterIndexed and zipWithNext
val x = filterIndexed { index, _ -> index % 2 == 0 }.zipWithNext()
val y = filterIndexed { index, _ -> index % 2 == 1 }.zipWithNext()
But the result is:
[x1, y1, x2, y2, x3, y3] => [(x1, x2), (x2, x3)], [(y1, y2), (y2, y3)]
If I understand correctly, the problem with the zipWithNext that you are using is that it doesn't "wrap around", i.e. output the final (x3, x1) or (y3, y1) pair, containing the last and first elements of the list.
You can fix this by simply declaring your own version of zipWithNext that does do this.
You can either do something like this:
fun <T> Iterable<T>.zipWithNextAndWrapAround(): List<Pair<T, T>> {
val zippedWithNext = zipWithNext()
if (zippedWithNext.isEmpty()) return zippedWithNext
return zippedWithNext + (zippedWithNext.last().second to zippedWithNext.first().first)
}
Or copy and paste over the original source code of zipWithNext and slightly modify it:
fun <T> Iterable<T>.zipWithNextAndWrapAround(): List<Pair<T, T>> {
val iterator = iterator()
if (!iterator.hasNext()) return emptyList()
val result = mutableListOf<Pair<T, T>>()
var current = iterator.next()
// remember what the first element was
val first = current
while (iterator.hasNext()) {
val next = iterator.next()
result.add(current to next)
current = next
}
// at last, add this pair
result.add(current to first)
return result
}
Usage:
val x = list.filterIndexed { index, _ -> index % 2 == 0 }.zipWithNextAndWrapAround()
val y = list.filterIndexed { index, _ -> index % 2 == 1 }.zipWithNextAndWrapAround()
Note that this is looping through the list twice. You can avoid that by writing your own version of partition called partitionIndexed.
The code could be something like:
inline fun <T> Iterable<T>.partitionIndexed(predicate: (Int, T) -> Boolean): Pair<List<T>, List<T>> {
val first = ArrayList<T>()
val second = ArrayList<T>()
forEachIndexed { index, element ->
if (predicate(index, element)) {
first.add(element)
} else {
second.add(element)
}
}
return Pair(first, second)
}
// usage:
val (x, y) = list.partitionIndexed { index, _ ->
index % 2 == 0
}.let { (a, b) ->
a.zipWithNextAndWrapAround() to b.zipWithNextAndWrapAround()
}
You could do something like this:
val lst = listOf(1, 2, 3, 4, 5, 6, 7, 8)
val intermediate = lst.chunked(2).map { it[0] to it[1] }.let { it + it[0] }
val x = intermediate.map { it.first }.zipWithNext()
val y = intermediate.map { it.second }.zipWithNext()
println(x) //[(1, 3), (3, 5), (5, 7), (7, 1)]
println(y) //[(2, 4), (4, 6), (6, 8), (8, 2)]
val input = listOf("x1", "y1", "x2", "y2", "x3", "y3")
val result = list
.withIndex()
.groupBy { it.index % 2 }
.map { entry -> entry.value.map { it.value } }
.map { (it + it[0]).zipWithNext() }
println(result)
Output:
[[(x1, x2), (x2, x3), (x3, x1)], [(y1, y2), (y2, y3), (y3, y1)]]
I wrote this goofy tuplize function:
fun foo(x: Int, y: Int) = 3 * x + 2 * y + 1
fun <T, U, R> tuplize(f: (T, U) -> R): ((Pair<T, U>) -> R) = { (a, b): Pair<T, U> -> f(a, b) }
val xs = listOf(Pair(1, 2), Pair(42, 23))
val f = tuplize(::foo)
val ys = xs.map(f)
It works, but I guess arrow-kt already has something nice build-in, and I just can't find it. Can you help me out? :)
(Sure, I could just use val ys = xs.map { (a, b) -> foo(a, b) }, but in this example, the goal is to express it in point-free style.)
In Code A, the parameters x1 and x2 use the same vaule, it works well.
I think I can improve Code A, so I write Code B, but it failed.
How can I assign x2 with the value of x1 ?
Code A
val stepWidth = step * index
it.drawChildnAxis(
x1 = stepWidth.toX, y1 = 0f.toY,
x2 = stepWidth.toX, y2 = yAxisLength.toY
)
fun Canvas.drawChildnAxis(x1:Float, y1:Float,x2:Float,y2:Float){
drawLine(
Offset(x = x1, y = y1),
Offset(x = x2, y = y2),
paintTableAxisChild
)
}
Code B
it.drawChildnAxis(
x1 = step * index.toX, y1 = 0f.toY,
x2 = x1, y2 = yAxisLength.toY
)
//The same
The x1 = ..., x2 = ... etc in your code are not actually assignment statements! They are named arguments.
There is no variable x1, x2 etc that becomes suddenly in scope at the function call, allowing you to assign values to it. This is just a bit of a syntax that lets you say the names of your parameters to make your code more readable, and sometimes resolve overload resolution ambiguities.
The syntax just so happened to be designed to look similar to assignments, making the left hand side look like a new variable just got declared. Would you still have this confusion if the syntax used : instead of =?
it.drawChildnAxis(
x1: stepWidth.toX, y1: 0f.toY,
x2: stepWidth.toX, y2: yAxisLength.toY
)
So x2 = x1 doesn't make sense - there is no such variable as x1 at that position. x1 is only the name of a parameter, which is only in scope when you are inside drawChildnAxis.
If you want to avoid repetition, just create a new variable yourself!
val x = stepWidth.toX
it.drawChildnAxis(
x1 = x, y1 = 0f.toY,
x2 = x, y2 = yAxisLength.toY
)
If you don't want x to be accessible afterwards, use a scope function:
stepWidth.toX.let { x ->
it.drawChildnAxis(
x1 = x, y1 = 0f.toY,
x2 = x, y2 = yAxisLength.toY
)
}
All of this is of course assuming that toX doesn't have side effects - calling its getter twice on the same thing gives the same value.
I have 2 lists containing objects of the same type and I am looking to merge these 2 lists.
Let's take an example:
List1 contains [A, B, C, E]
List2 contains [A, D]
what I need [A, B, C, E, D]
I absolutely want the identical elements (here the object A) to be those of my list 1.
It does not matter if the order of the items is not kept.
If you want to keep identical elements from list1, you should not use distinct. Your code should be explicit on this business rule to avoid future errors.
An example :
class Elt(private val id: Int, private val content: String) {
open fun equals(other: Elt): Boolean {
return this.id == other.id
}
override fun toString(): String {
return "$id -> $content"
}
}
fun main(args: Array<String>) {
val l1 = listOf(Elt(1,"L1"), Elt(2,"L1"), Elt(3,"L1"), Elt(4,"L1"))
val l2 = listOf(Elt(1,"L2"), Elt(5,"L2"))
val l4 = l2 + l1
println(l4.distinct()) // Elt 1 comes from L2
val l5 = l1 + l2
println(l5.distinct()) // Elt 1 comes from L1
val l6 = l2.toMutableList().apply { addAll(l1) }.distinct()
println(l6.distinct()) // Elt 1 comes from L2
}
It will print:
[1 -> L1, 2 -> L1, 3 -> L1, 4 -> L1, 1 -> L2, 5 -> L2]
[1 -> L2, 5 -> L2, 1 -> L1, 2 -> L1, 3 -> L1, 4 -> L1]
[1 -> L1, 2 -> L1, 3 -> L1, 4 -> L1, 1 -> L2, 5 -> L2]
[1 -> L2, 5 -> L2, 1 -> L1, 2 -> L1, 3 -> L1, 4 -> L1]
If you remove duplicate in list2 before adding the elements, you will ensure that you keep identical ielement from list1:
val l3 = l1 + (l2 - l1.intersect(l2))
println(l3)
Simple case:
val lista = listOf( 1,2,3 )
val listb = mutableListOf( 1,4,5 ).apply { addAll(lista) }.distinct()
// result listb -> [1, 4, 5, 2, 3]
To add two string list
val a = listOf("a","b" ,"c" , "e")
val b = listOf("a", "d")
val c = a + b
To have only distinct values,
val d = c.distinct()
An alternative to the above solutions: use HashSet. Set collections don't support multiple occurrences of the same element so when you add A twice, the second one simply gets discarded.
var s: HashSet<String> = HashSet<String>()
s.addAll(listOf("A", "B", "C", "E"))
s.addAll(listOf("A", "D"))
var l = s.toList()
Since HashSet uses hashing under the hood, you get O(1) complexity on most of its operations.
For example if you were to have
datatype 'a DT =
thingy of {field1: int, field2: int}
| Empty DT;
How would you be able to create "thingy" with its fields filled in, within a function? Assuming you have the values you want for each field.
- datatype 'a DT = thingy of {x: int, y: int} | Empty;
datatype 'a DT = Empty | thingy of {x:int, y:int}
- thingy {x = 5, y = 4};
val it = thingy {x=5,y=4} : 'a DT
- fun f x y = thingy {x = x, y = y};
val f = fn : int -> int -> 'a DT