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;
Related
void main() {
var a = [];
var b = [];
print(a == []);
print(a == b);
var c = ['c'];
print(c == ['c']);
var d = 'd';
print(d == 'd');
}
false
false
false
true
This baffles me. Why is it like this?
Edit: I just realised it doesn't have to be empty
The Dart platform libraries have chosen to, generally, not provide a native == implementation for mutable collections (or mutable objects in general).
Checking that [] == [] seems obvious, but they are different objects.
If you do:
var l1 = [];
var l2 = [];
print(l1 == l2);
l1.add("an element");
print(l1 == l2);
do you expect the two ==s to give the same result?
If not, how about:
var l1 = <int>[];
var l2 = <int>[];
var m = <List<int>>{l1}; // A set of lists.
print(m.contains(l2)); // true or false?
l1.add(1);
print(m.contains(l2)); // true or false?
On top of those concerns, which other languages also have, Dart have reified generics. An empty List<int> and an empty List<num> is not the same object, and runtime type-checks can tell the difference.
Should <int>[] == <num>[] then? Definitely safest to say no!
All in all, Dart does not have == on List (or other collections) recursively check whether the two lists contain the same (or equal) elements.
It only says true if it's the exact same object.
If you want to check list-element-equality, you can use the Equality classes from package:collection, like ListEquality and the custom hash/equals parameters to the hash map/set classes.
Example:
const intListEquality = ListEquality<int>();
var listMap = LinkedHashMap<List<int>, String>(
equals: intListEquality.equals,
hashCode: intListEquality.hash);
listMap[[1]] = "42";
print(listMap([1]); // 42
It has to do with equality checks on Dart. They don't evaluate equality based on values, rather, they use hashCodes.
If you're using Dart in the context of Flutter you can use the native Flutter Method listEquals for value based comparison using Lists.
If you want value based equality on other Objects you must override the == operator or use packages like Equatable that helps you with that.
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
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))
}
I'm trying to write the get method for a key, value pair implemented using a list. I want to use the Option type as I heard its good for this but I'm new to Scala and I'm not really sure how to use it in this case...
This is as far as I got, only the method header.
def get(key : String): Option[Any] = {}
My guess is you are looking for something like this:
class KeyValueStore(pairs: List[(String, Any)]) {
def get(key: String): Option[Any] = pairs.collectFirst {
case (k, v) if k == key => v
}
}
This uses the collectFirst method for sequences. If you want a more "do it yourself" approach, this should work:
def get(key: String): Option[Any] = {
def search(xs: List[(String, Any)]): Option[Any] = {
xs match {
case List() => None //end of list and key not found. We return None
case (k, v) :: rest if k == key => Some(v) // found our key. Returning some value
case _ :: rest => search(rest) // not found until nou. Carrying on with the rest of the list
}
search(pairs)
}
}
You can turn a List of Pairs into a Map:
class Store[K, V](values: List[(K, V)]) {
val map = values.toMap
def get(key: K): Option[V] = map get key
}
Although #Marius' collectFirst version is probably the most elegant (and maybe a little bit faster as it only uses one closure), I find it more intuitive to use find for your problem :
def get[A, B](key: A, pairs: List[(A, B)]): Option[B] = pairs.find(_._1 == key).map(_._2)
In case you were wondering (or need high performance), you will need either #Marius' recursive or the following imperative version which may look more familiar (although less idiomatic):
def get[A, B](key: A, pairs: List[(A, B)]): Option[B] = {
var l = pairs
var found: Option[B] = None
while (l.nonEmpty && found.isEmpty) {
val (k, v) = l.head
if (k == key) {
found = Some(v)
} else {
l = l.tail
}
}
found
}
What you must understand is that Option[B] is a class that may either be instantiated to None (which replaces and improves the null reference used in other languages) or Some(value: B). Some is a case class, which allows, among other neat features, to instantiate it without the new keyword (thanks to some compiler magic, Google Scala case class for more info). You can think of Option as a List which may contain either 0 or 1 element: most operations that can be done on sequences can also be applied to Options (such as map in the find version).
I'm trying to unit-test some Scala that is very collection-heavy. These collections are returned as Iterable[T], so I am interested in the contents of the collection, even if the underlying types differ. This is actually two related problems:
How do I assert that two ordered collections contain the same sequence of elements?
How do I assert that two unordered collections contain the same set of elements?
In summary, I'm looking the Scala-equivalent of NUnit's CollectionAssert.AreEqual (ordered) and CollectionAssert.AreEquivalent (unordered) in ScalaTest:
Set(1, 2) should equal (List(1, 2)) // ordered, pass
Iterable(2, 1) should equal (Iterable(1, 2)) // unordered, pass
Meanwhile you can use
Iterable(2, 1) should contain theSameElementsAs Iterable(1, 2)
To test the ordered set you have to convert it to a sequence.
Set(1, 2).toSeq should contain theSameElementsInOrderAs List(1, 2)
You could try .toSeq for ordered collections and .toSet for unordered, which captures what you want as far as I understand it.
The following passes:
class Temp extends FunSuite with ShouldMatchers {
test("1") { Array(1, 2).toSeq should equal (List(1, 2).toSeq) }
test("2") { Array(2, 1).toSeq should not equal (List(1, 2).toSeq) }
test("2b") { Array(2, 1) should not equal (List(1, 2)) }
test("3") { Iterable(2, 1).toSet should equal (Iterable(1, 2).toSet) }
test("4") { Iterable(2, 1) should not equal (Iterable(1, 2)) }
}
BTW a Set is not ordered.
edit: To avoid removing duplicate elements, try toSeq.sorted. The following pass:
test("5") { Iterable(2, 1).toSeq.sorted should equal (Iterable(1, 2).toSeq.sorted) }
test("6") { Iterable(2, 1).toSeq should not equal (Iterable(1, 2).toSeq) }
edit 2: For unordered collections where elements cannot be sorted, you can use this method:
def sameAs[A](c: Traversable[A], d: Traversable[A]): Boolean =
if (c.isEmpty) d.isEmpty
else {
val (e, f) = d span (c.head !=)
if (f.isEmpty) false else sameAs(c.tail, e ++ f.tail)
}
e.g. (note use of symbols 'a 'b 'c which have no defined ordering)
test("7") { assert( sameAs(Iterable(2, 1), Iterable(1, 2) )) }
test("8") { assert( sameAs(Array('a, 'c, 'b), List('c, 'a, 'b) )) }
test("9") { assert( sameAs("cba", Set('a', 'b', 'c') )) }
Alternative sameAs implementation:
def sameAs[A](c: Traversable[A], d: Traversable[A]) = {
def counts(e: Traversable[A]) = e groupBy identity mapValues (_.size)
counts(c) == counts(d)
}