hi all, i want to concatenate two lists both of wh...
# getting-started
g
hi all, i want to concatenate two lists both of which can be
null
, the only thing I came up with is this:
Copy code
return if (createdChildren == null) {
            deletedChildren
        } else {
            if (deletedChildren == null) {
                createdChildren
            } else {
                createdChildren + createdChildren
            }
        }
is there an easier way to do this?
or same thing with when:
Copy code
return when (createdChildren) {
            null -> deletedChildren
            else -> when (deletedChildren) {
                null -> createdChildren
                else -> createdChildren + createdChildren
            }
        }
ideally i would like to just
Copy code
return createdChildren + deletedChildren
but that does not work
j
what do you want to return when both lists are
null
?
g
null
the return type is also list that can be
null
j
You can use
orEmpty()
to change a nullable List to an empty List. So then you can write
Copy code
return listA.orEmpty() + listB.orEmpty()
(that would return an empty list if both are null) Or, if you don’t want to add empty lists together you can use just one when:
Copy code
return when {
  listA == null -> listB
  listB == null -> listA
  else -> listA + listB
}
g
@Jaap Beetstra that actually looks quite cool. Not as cool as
listA + listB
but still I like it 😄
j
You can already do that
Copy code
operator fun <E> List<E>?.plus(o: List<E>?): List<E>? = when {
    this == null -> o
    o == null -> this
    else -> this + o
}
g
@Jaap Beetstra I didn't know that. But it would be good if devs add the operator to the library
thanks I updated my request
j
or maybe you want a
+?
operator ^ ^
g
@Julien Plissonneau Duquène why? I think
+
reflects the meaning of what I want to do
@Julien Plissonneau Duquène when one of operands is
null
, kotlin can just use
+
operator of the optional type
@Julien Plissonneau Duquène sorry, please ignore my previous message. I think
+
for the non-
null
types should also accept
null
on the right operator and then return the left operator.
j
the issue is on the left side actually,
listOf("a").plus(null)
will evaluate to a
List<String?>
with two elements
"a"
and
null
but you can't do
null.plus(...)
also be careful with
plus()
as it won't add elements but the whole list if the list itself is nullable:
Copy code
println("a: " + (listOf<String?>("abc").plus(listOf("def", "ghi") as List<String?>)))
println("b: " + (listOf<String?>("abc").plus(listOf("def", "ghi") as List<String?>?)))
Copy code
a: [abc, def, ghi]
b: [abc, [def, ghi]]
though the
when
blocks above would work correctly
g
sorry, but I want a really simple thing, concatanate two lists, if one of them is null, then it's the same as if it was empty, so add nothing. 😄
d
If you want something more clever, there's this one-liner
Copy code
listOfNotNull(createdChildren, deletedChildren).reduceOrNull { l, r -> l + r }
Conveniently that would work for any number of lists as well
r
@Gasan > sorry, but I want a really simple thing, concatanate two lists, if one of them is null, then it's the same as if it was empty, so add nothing. That is indeed really simple, but it's not actually what you want (at least, it's not actually what you're asking for). What you want is for it to happen with the
+
operator, which is more complicated. For example, we want both of these to be true:
List<T> + T == List<T>
List<T> + List<T> == List<T>
But that's actually ambiguous. What if we have:
List<Any?> + List<Any?>
? That actually matches both cases (since
List<Any?>
is
Any?
), so which one takes precedence? And should that be resolved statically or dynamically? For example, say I have:
Copy code
fun addThem(a: List<Any?>?, b: Any?) = a + b
and I call it with
addThem(listOf(), listOf<Any?>())
. If I resolve the call statically,
b
is added as an element in
a
. If I resolve it dynamically,
b
is concatenated to
a
. Neither option is inherently more "correct" (it entirely depends on the use case). Now what happens if I call it with
addThem(listOf(),  null)
. Should I expect a list with one element more than
a
, or just
a
itself? Does that answer change if I do
addThem(listOf(), null as List<Any>?)
? TL;DR: Your issue isn't with the concept of concatenating lists, it's with the ambiguity of using
+
to do it, so a named function would probably be a better option to make the intent clear (you could even make it an infix to so it's operator-like e.g.:
List<T>? concat List<T>?
).
g
@Ruckus ok, so then kotlin needs 2 operators, one to add element and another to add all. Actually, kotlin already has a
union
, so the only thing to fix is to remove
union
logic from
+
operator :D
r
Sure, but what operator would you recommend? The concept of "adding" is somewhat ambiguous by its very nature (i.e. you can add two planks together, which is concatenation, and you can add something to a bag, which is insertion), and
+
is the common operator for "adding", so which one is the correct one? (There isn't a single correct answer unfortunately).
Since your case is effectively a union (more accurately a concatenation), your conclusion implies what you want to do shouldn't be done with
+
g
> (i.e. you can add two planks together, which is concatenation, and you can add something to a bag, which is insertion) right. The program returns "left or right or both"