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))
}
Related
I'd like to check a List<Int?> for nulls and if no nulls exist, then allow the List to proceed as a List<Int>, otherwise use an emptyList()
This is what I came up with:
var noNullElements: List<Int> = emptyList()
if (listWithPossibleNullElements.all { it != null }) {
noNullElements = listWithPossibleNullElements as List<Int>
}
//do something with noNullElements list
I'd be shocked if this was the best, most idiomatic way.
Solution
There are many acceptable solutions below, but the one I chose was a combination of the solutions
fun getSafeList(list: List<Int?>): List<Int> =if (null in list) emptyList() else list.requireNoNulls()
Here's a one liner extension function:
inline fun <reified T : Any> Iterable<T?>.filterNoneNull(): Iterable<T>? =
takeIf { null !in it }?.requireNoNulls()
takeIf {} - a scope function that will return null if the lambda evaluates to false
null !in it - use the in operator function to determine if the list contains null
finally, if takeIf {} returns a non-null value, filterNotNull() will convert the element type from the nullable T? to non-null T
Note that it will return null if the provided Iterable<T?> contains null, else List<T>, in case it's important to differentiate between the provided list being empty, or containing null. An alternative can be provided using the elvis operator.
Example usage - run in Kotlin Playground:
fun main() {
val listA: List<Int?> = listOf(1, 2, 3, null)
// since listA contains 'null', the result is 'null'
println(listA.filterNoneNull())
// prints: null
// provide an alternative, using the elvis operator
println(listA.filterNoneNull() ?: emptyList<Int>())
// prints: []
val listB: List<Int> = listOf(1, 2, 3)
// since listB does not contain null, the same list is returned
println(listB.filterNoneNull())
// prints: [1, 2, 3]
}
inline fun <reified T : Any> Iterable<T?>.filterNoneNull(): Iterable<T>? =
takeIf { null !in it }?.requireNoNulls()
Note: I edited the question and replaced filterNotNull() with requireNoNulls(), as the latter will not instantiate a new list
Check with contains for the existence of at least one null value:
val noNullElements = if (list.contains(null)) emptyList() else list.map { it!! }
Edit: Added this extension function:
fun List<Int?>.castToIntOrEmpty() =
if (this.contains(null)) emptyList() else this.map { it as Int }
val noNullElements = list.castToIntOrEmpty()
Example with no null elements:
val list: List<Int?> = List(10) { it }
// list is: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
val noNullElements = if (list.contains(null)) emptyList() else list.map { it!! }
println(noNullElements) // Output: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Example with some (or all) null elements:
val list: List<Int?> = List(10) { if (it % 2 == 0) it else null }
// list is: [0, null, 2, null, 4, null, 6, null, 8, null]
val nullElements = if (list.contains(null)) emptyList() else list.map { it!! }
println(nullElements) // Output: []
I think your problem is you want to take a generic List<Int?> type and treat it as a List<Int>, which means casting it. Because List<Int?> isn't a subtype of List<Int> (although the inverse is true, as Joffrey points out in the comments) you basically have to perform an unchecked cast in this situation:
if (list.contains(null)) emptyList else list as List<Int>
The compiler isn't smart enough to determine that this is a safe cast at runtime (and you have to be able to make that guarantee yourself, e.g. ensuring the list isn't being modified elsewhere to include a null after the check passes) so you'll get an unchecked cast warning. The documentation on generics goes into your options, but if you really want to pass in a List<Int?> and treat it as a List<Int>, you'll have to suppress that warning:
An unchecked cast warning can be suppressed by annotating the statement or the declaration where it occurs with #Suppress("UNCHECKED_CAST")
I'd argue the more idiomatic approach is to just create a new List<Int> instead of trying to cast the original one to a different generic type, or to make the original list provider pass a List<Int>? instead (i.e. null if there were any non-null elements) which is maybe just moving the problem - but it really depends on what you're doing.
You can easily create filtered copies and compare it to the original to decide whether to use it, or write a for loop building a new list that short-circuits if you hit a null, all those kinds of things. If you want to check first and only copy if you need it, lukas.j's answer has more examples (including a couple that throw exceptions, like requireNoNulls()) so you have a lot of options, and there are different tradeoffs - it really depends on what you're doing and where the bottlenecks are (if any). Things can get a bit awkward when you work with generics, so some situations don't end up looking as neat as you'd like!
Try using filterNotNull:
val noNullElements: List<Int> = listWithPossibleNullElements.filterNotNull()
In Python, the index -1 is the first element from the right in a list.
How can the last nth element of a list be retrieved in Kotlin in the most idiomatic way? To write an extension for it?
#dey already provided a solution for querying negative indexes, however I think you were looking for an extension function to query lists using both positive and negative indexes and using brackets. This is what you can do:
Extension functions cannot replace existing methods in classes. Since the getter operator (which is the one that allows you to use brackets as in your example) already exists in the List class, you won't be able to create an extension like that. However, you can create a new extension method with the same behaviour:
fun <E> List<E>.gett(index: Int): E = if (index < 0) {
this[size + index]
} else {
this[index]
}
#Test
fun testGett(){
val list = (0..10).toList()
assertEquals(10, list.gett(-1))
assertEquals(0, list.gett(-11))
assertEquals(1, list.gett(1))
}
#Test(expected = IndexOutOfBoundsException::class)
fun testException(){
(0..10).toList().gett(-12)
}
If you really want to have your own implementation using the brackets, you need to create your own list class (by extending List interface) and override the get operator function.
(For the sake of clarity, I'm extending from ArrayList, so I don't need to implement other members)
class MyList<E> : ArrayList<E>() {
override operator fun get(index: Int): E = if (index < 0) {
super.get(size + index)
} else {
super.get(index)
}
}
#Test
fun testMyList(){
val list = MyList<Int>().apply {
add(1);add(2);add(3)
}
assertEquals(3, list[-1])
assertEquals(1, list[-3])
assertEquals(2, list[1])
}
I think, there is no beautiful way to do this. I think, the best you can do is this (all code snippets contain function implementation and example to get the last element):
fun List.last(int n) = get(size() - n)
list.last(1)
But now last elements are indexed from 1 (more or less like in python). If you want to index from 0, you need to subtract additional 1:
fun List.last(int n) = get(size() - n - 1)
list.last(0)
Or if you want to use indexes like in python, you need to add n:
fun List.last(int n) = get(size() + n)
list.last(-1)
You can use last.
For example
println(listOf(1, 2, 3).last()) // outputs: 3
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.
I'm comparing two lists in Dart like this:
main() {
if ([1,2,3] == [1,2,3]) {
print("Equal");
} else {
print("Not equal");
}
}
But they are never equal. There doesn't seem to be an equal() method in the Dart API to compare Lists or Collections. Is there a proper way to do this?
To complete Gunter's answer: the recommended way to compare lists for equality (rather than identity) is by using the Equality classes from the following package
import 'package:collection/collection.dart';
Edit: prior to 1.13, it was import 'package:collection/equality.dart';
E.g.:
Function eq = const ListEquality().equals;
print(eq([1,'two',3], [1,'two',3])); // => true
The above prints true because the corresponding list elements that are identical(). If you want to (deeply) compare lists that might contain other collections then instead use:
Function deepEq = const DeepCollectionEquality().equals;
List list1 = [1, ['a',[]], 3];
List list2 = [1, ['a',[]], 3];
print( eq(list1, list2)); // => false
print(deepEq(list1, list2)); // => true
There are other Equality classes that can be combined in many ways, including equality for Maps. You can even perform an unordered (deep) comparison of collections:
Function unOrdDeepEq = const DeepCollectionEquality.unordered().equals;
List list3 = [3, [[],'a'], 1];
print(unOrdDeepEq(list2, list3)); // => true
For details see the package API documentation. As usual, to use such a package you must list it in your pubspec.yaml:
dependencies:
collection: any
For those using Flutter, it has the native function listEquals, which compares for deep equality.
import 'package:flutter/foundation.dart';
var list1 = <int>[1, 2, 3];
var list2 = <int>[1, 2, 3];
assert(listEquals(list1, list2) == true);
import 'package:flutter/foundation.dart';
var list1 = <int>[1, 2, 3];
var list2 = <int>[3, 2, 1];
assert(listEquals(list1, list2) == false);
Note that, according to the documentation:
The term "deep" above refers to the first level of equality: if the elements are maps, lists, sets, or other collections/composite objects, then the values of those elements are not compared element by element unless their equality operators (Object.operator==) do so.
Therefore, if you're looking for limitless-level equality, check out DeepCollectionEquality, as suggested by Patrice Chalin.
Also, there's setEquals and mapEquals if you need such feature for different collections.
Collections in Dart have no inherent equality. Two sets are not equal, even if they contain exactly the same objects as elements.
The collection library provides methods to define such an equality. In this case, for
example
IterableEquality().equals([1,2,3],[1,2,3])
is an equality that considers two lists equal exactly if they contain identical elements.
I just stumbled upon this
import 'package:collection/equality.dart';
void main(List<String> args) {
if (const IterableEquality().equals([1,2,3],[1,2,3])) {
// if (const SetEquality().equals([1,2,3].toSet(),[1,2,3].toSet())) {
print("Equal");
} else {
print("Not equal");
}
}
more info https://github.com/dart-lang/bleeding_edge/tree/master/dart/pkg/collection
You can use the built-in listEquals, setEquals, and mapEquals function to check for equality now.
Here's the link to the docs: https://api.flutter.dev/flutter/foundation/listEquals.html
While this question is rather old, the feature still hasn't landed natively in Dart. I had a need for deep equality comparison (List<List>), so I borrowed from above now with recursive call:
bool _listsAreEqual(list1, list2) {
var i=-1;
return list1.every((val) {
i++;
if(val is List && list2[i] is List) return _listsAreEqual(val,list2[i]);
else return list2[i] == val;
});
}
Note: this will still fail when mixing Collection types (ie List<Map>) - it just covers List<List<List>> and so-on.
Latest dart issue on this seems to be https://code.google.com/p/dart/issues/detail?id=2217 (last updated May 2013).
A simple solution to compare the equality of two list of int in Dart can be to use Sets (without checking the order of the element in the list) :
void main() {
var a = [1,2,3];
var b = [1,3,2];
var condition1 = a.toSet().difference(b.toSet()).isEmpty;
var condition2 = a.length == b.length;
var isEqual = condition1 && condition2;
print(isEqual); // Will print true
}
The Built Collections library offers immutable collections for Dart that include equality, hashing, and other goodies.
If you're doing something that requires equality there's a chance you'd be better off with immutability, too.
http://www.dartdocs.org/documentation/built_collection/0.3.1/index.html#built_collection/built_collection
Sometimes one want to compare not the lists themselves but some objects (class instances) that contain list(s)
Let you have a class with list field, like
class A {
final String name;
final List<int> list;
A(this.name, this.list);
}
and want to compare its instances, you can use equatable package
class A extends Equatable {
final String name;
final List<int> list;
A(this.name, this.list);
#override
List<Object> get props => [name, list];
}
and then simply compare objects using ==
final x = A('foo', [1,2,3]);
final y = A('foo', [1,2,3]);
print(x == y); // true
The most convenient option would be to create an extension method on the List class.
extension on List {
bool equals(List list) {
if(this.length!=list.length) return false;
return this.every((item) => list.contains(item));
}
}
What the above code does is that it checks to see if every single item from the first list (this) is contained in the second list (list). This only works, however, if both lists are of the same length. You can, of course, substitute in one of the other solutions or modify this solution, I am just showing this for the sake of convenience. Anyway, to use this extension, do this:
List list1, list2
list1.equals(list2);
Before things work, you can use this:
/**
* Returns true if the two given lists are equal.
*/
bool _listsAreEqual(List one, List two) {
var i = -1;
return one.every((element) {
i++;
return two[i] == element;
});
}
Would something like this work for you?
try {
Expect.listEquals(list1, list2);
} catch (var e) {
print("lists aren't equal: $e");
}
Example:
main() {
List<int> list1 = [1, 2, 3, 4, 5];
List<int> list2 = new List.from(list1);
try {
Expect.listEquals(list1, list2);
} catch (var e) {
print("lists aren't equal: $e");
}
list1.removeLast();
try {
Expect.listEquals(list1, list2);
} catch (var e) {
print("Lists aren't equal: $e");
}
}
Second try prints:
Lists aren't equal: Expect.listEquals(list length, expected: <4>, actual: <5>) fails
bool areListsEqual(var list1, var list2) {
// check if both are lists
if(!(list1 is List && list2 is List)
// check if both have same length
|| list1.length!=list2.length) {
return false;
}
// check if elements are equal
for(int i=0;i<list1.length;i++) {
if(list1[i]!=list2[i]) {
return false;
}
}
return true;
}
A simple solution to compare the equality of two list of int in Dart can be to compare it as String using join() :
var a = [1,2,3,0];
var b = [1,2,3,4];
a.join('') == b.join('') ? return true: return false;
I have a list l:List[T1] and currently im doing the following:
myfun : T1 -> Option[T2]
val x: Option[T2] = l.map{ myfun(l) }.flatten.find(_=>true)
The myfun function returns None or Some, flatten throws away all the None's and find returns the first element of the list if any.
This seems a bit hacky to me. Im thinking that there might exist some for-comprehension or similar that will do this a bit less wasteful or more clever.
For example: I dont need any subsequent answers if myfun returns any Some during the map of the list l.
How about:
l.toStream flatMap (myfun andThen (_.toList)) headOption
Stream is lazy, so it won't map everything in advance, but it won't remap things either. Instead of flattening things, convert Option to List so that flatMap can be used.
In addition to using toStream to make the search lazy, we can use Stream::collectFirst:
List(1, 2, 3, 4, 5, 6, 7, 8).toStream.map(myfun).collectFirst { case Some(d) => d }
// Option[String] = Some(hello)
// given def function(i: Int): Option[String] = if (i == 5) Some("hello") else None
This:
Transforms the List into a Stream in order to stop the search early.
Transforms elements using myFun as Option[T]s.
Collects the first mapped element which is not None and extract it.
Starting Scala 2.13, with the deprecation of Streams in favor of LazyLists, this would become:
List(1, 2, 3, 4, 5, 6, 7, 8).to(LazyList).map(function).collectFirst { case Some(d) => d }
Well, this is almost, but not quite
val x = (l flatMap myfun).headOption
But you are returning a Option rather than a List from myfun, so this may not work. If so (I've no REPL to hand) then try instead:
val x = (l flatMap(myfun(_).toList)).headOption
Well, the for-comprehension equivalent is pretty easy
(for(x<-l, y<-myfun(x)) yield y).headOption
which, if you actually do the the translation works out the same as what oxbow_lakes gave. Assuming reasonable laziness of List.flatmap, this is both a clean and efficient solution.
As of 2017, the previous answers seem to be outdated. I ran some benchmarks (list of 10 million Ints, first match roughly in the middle, Scala 2.12.3, Java 1.8.0, 1.8 GHz Intel Core i5). Unless otherwise noted, list and map have the following types:
list: scala.collection.immutable.List
map: A => Option[B]
Simply call map on the list: ~1000 ms
list.map(map).find(_.isDefined).flatten
First call toStream on the list: ~1200 ms
list.toStream.map(map).find(_.isDefined).flatten
Call toStream.flatMap on the list: ~450 ms
list.toStream.flatMap(map(_).toList).headOption
Call flatMap on the list: ~100 ms
list.flatMap(map(_).toList).headOption
First call iterator on the list: ~35 ms
list.iterator.map(map).find(_.isDefined).flatten
Recursive function find(): ~25 ms
def find[A,B](list: scala.collection.immutable.List[A], map: A => Option[B]) : Option[B] = {
list match {
case Nil => None
case head::tail => map(head) match {
case None => find(tail, map)
case result # Some(_) => result
}
}
}
Iterative function find(): ~25 ms
def find[A,B](list: scala.collection.immutable.List[A], map: A => Option[B]) : Option[B] = {
for (elem <- list) {
val result = map(elem)
if (result.isDefined) return result
}
return None
}
You can further speed up things by using Java instead of Scala collections and a less functional style.
Loop over indices in java.util.ArrayList: ~15 ms
def find[A,B](list: java.util.ArrayList[A], map: A => Option[B]) : Option[B] = {
var i = 0
while (i < list.size()) {
val result = map(list.get(i))
if (result.isDefined) return result
i += 1
}
return None
}
Loop over indices in java.util.ArrayList with function returning null instead of None: ~10 ms
def find[A,B](list: java.util.ArrayList[A], map: A => B) : Option[B] = {
var i = 0
while (i < list.size()) {
val result = map(list.get(i))
if (result != null) return Some(result)
i += 1
}
return None
}
(Of course, one would usually declare the parameter type as java.util.List, not java.util.ArrayList. I chose the latter here because it's the class I used for the benchmarks. Other implementations of java.util.List will show different performance - most will be worse.)