Comparing collection contents with ScalaTest - unit-testing

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)
}

Related

How can I make a list of tuples in scala in a function?

I am trying to make a list filled with tuples, in a recursive function, so the list can get bigger while iterating. I just don't know how to achieve this in scala.
def tryToClaimPosition(player: Int, horizontal: Int , vertical: Int , list: List[(Int, Int)]): Unit = {
if (vertical == gridsize - 1) {
println(list);
}
if (horizontal < gridsize - 1) {
if (gameBoard(horizontal)(vertical) == player) {
val tuple = (horizontal,vertical)
val list2 = list :: tuple
tryToClaimPosition(player, horizontal + 1, vertical, list2)
else {
tryToClaimPosition(player, horizontal + 1, vertical, list)
}
}
As you see in the snippet above I have a List of tuples and I have a tuple, but I cannot add it.
I'd use immutable list instead, that would be an accumulator. This would be in a more functional way. The function would have no side effect and would return a resulting List:
def tryToClaimPosition(player: Int, horizontal: Int , vertical: Int , list: List[(Int, Int)]): List[(Int, Int)] = {
if (vertical == gridsize - 1) {
list.reverse
}
if (horizontal < gridsize - 1) {
if (gameBoard(horizontal)(vertical) == player) {
val tuple = (horizontal, vertical)
tryToClaimPosition(player,horizontal + 1, vertical, tuple::list)
else {
tryToClaimPosition(player,horizontal + 1, vertical, list)
}
} else Nil
}
Notice, that I have prepended the tuple to the List, because it takes constant time (appending is proportional to the size of the List). That's why I'm returning list.reverse, so that the original ordering is maintained.
Also I asked about the case when horizontal > gridsize - 1. Your program does nothing, so I assume, nothing should be returned, hence Nil in else clause. It is needed, so that there is always a value returned from the function.
Your code has many glaring logic and syntax errors. Most pertinent to your question is that fact that list :: tuple won't compile because the right side of the :: method must be a List.
Use :: to prepend, not append, to a List.

Scala reduce a List based on a condition

I have a List of certain type that I want to reduce based on a condition. I have a type where the Interval is a DateTime interval with a start and an end:
case class MyType(a: Interval, value: Double)
I have got a List[MyType] entries that I want to reduce to a List[MyType] based on MyType that contains same DateTime and value. I do not want to go over the List twice which I already do.
Say I have:
val a = MyType(interval1, 2)
val b = MyType(interval2, 2)
val c = MyType(interval3, 1)
val d = MyType(interval4, 6)
val e = MyType(interval5, 2)
val original = List(a, b, c, d, e)
I have to now reduce the original List based on the following conditions:
1. interval should be continuous, then take the start of the first entry and the end of the second entry
2. the double value should be the same
So assuming that interval1, interval2 are continuous, the result should look like:
val result = Seq(MyType(new Interval(a.interval.start, b.interval.end),2), c, d, e)
Is there a much more elegant solution or an idea?
In the reduce function, check if the condition is true, and if it is, return the current accumulator instead of what would you otherwise compute.
Here's how you would sum only even numbers:
Seq(1,4,6,3).foldLeft(0)( (acc, a) =>
if (a % 2 == 0) acc + a else acc
)
res5: Int = 10
Response to the edited question: It appears you have some conditions that have to hold about the consecuitve elements. Then you can apply the function .sliding.
Seq(a,b,c,d,e).sliding(2).foldLeft(0)(
case (acc, Seq(MyType(ai, a), MyType(bi, b))) =>
if (ai.max == bi.min) acc + a else acc
)
Buuut... You have probably guessed it would not be as performant as you would like. I hope you are not doing any premature optimization, because you know, that's the root of all evil. But if you really need performance, rewrite the code in terms of while loops (fall back to Java).
This should work:
def reduce(xs: List[MyType]) = {
xs match {
case a :: b :: tail =>
if(a.interval.end == b.interval.start && a.value == b.value)
reduce(MyType(new Interval(a.interval.start, b.interval.end) a.value) :: tail)
else
a :: reduce(b :: tail)
case _ => xs
}
}
The if condition might need minor tweaking depending on your exact needs, but the algorithm should work.
Given a list xs
If the first two items a and b can be merged into c, merge them and go back to step 1 with xs = c :: tail
If a and b cannot be merged, try reducing all elements but the first, and append the result to a
Otherwise (list has 1 element or is empty), return xs
Pay attantion that your task could result in multiple distinct solutions, which cannot be further reduced.
So as result you will get a set of solutions: Set[Set[MyType]]
I use Set[MyType] instead of proposed List[MyType] and Seq[MyType] because order is not important and my answer needs possibility to compare different solutions (in order to avoid duplicates).
My answer doesn't make assumptions about order of items, any order is OK.
Besides that in order to simplify the code I have replaced Interval with 2 fields from and to, which can be easily converted.
Here is the code for reduction:
case class MyType(from: Long, to: Long, value: Double)
object MyType {
//Returns all possible varians of reduced source.
//If reduction is not possible, returns empty set.
private def strictReduce(source: Set[MyType]): Set[Set[MyType]] = {
if (source.size <= 1) {Set.empty} else {
val active = source.head //get some item
val otherItems = source.tail //all other items
val reducedWithActive: Set[Set[MyType]] = otherItems.flatMap {
case after if active.to == after.from =>
//we have already found a reduction (active->after),
// so further reductions are not strictly required
reduce(otherItems - after + MyType(active.from, after.to, active.value))
case before if before.to == active.from =>
//we have already found a reduction (before->active),
// so further reductions are not strictly required
reduce(otherItems - before + MyType(before.from, active.to, active.value))
case notContinuos => Set.empty[Set[MyType]]
}
//check if we can reduce items without active
val reducedIgnoringActive = strictReduce(otherItems).
//if so, re-insert active and try to reduce it further, but not strictly anymore
flatMap (reducedOther => reduce(reducedOther + active))
reducedWithActive ++ reducedIgnoringActive
}
}
//Returns all possible varians of reduced source.
//If reduction is not possible, returns source as single result.
private def reduce(source: Set[MyType]): Set[Set[MyType]] = strictReduce(source) match {
case empty if empty.isEmpty => Set(source)
case reduced => reduced
}
//Reduces source, which contains items with different values
def reduceAll(source: Set[MyType]): Set[Set[MyType]] = source.
groupBy(_.value). //divide by values, because they are not merge-able
mapValues(reduce). //reduce for every group
values.reduceLeft((solutionSetForValueA, solutionSetForValueB) =>
//merge solutions for different groups
for(subSolutionForValueA <- solutionSetForValueA;
subSolutionForValueB <- solutionSetForValueB)
yield (subSolutionForValueA ++ subSolutionForValueB) //merge subSolutions
)
}
And here is the sample, which uses it:
object Example extends App {
val source = Set(
MyType(0L, 1L, 1.0),
MyType(1L, 2L, 2.0), //different value
MyType(1L, 3L, 1.0), //competing with next
MyType(1L, 4L, 1.0), //competing with prev
MyType(3L, 5L, 1.0), //joinable with pre-prev
MyType(2L, 4L, 2.0), //joinable with second
MyType(0L, 4L, 3.0) //lonely
)
val solutions: Set[Set[MyType]] = MyType.reduceAll(source)
//here you could choose the best solution (for example by size)
//printing out
solutions.foreach(solution => println(solution.toList.sortBy(_.from).sortBy(_.value).
map(item => s"${item.from}->${item.to}(${item.value})").mkString(", ")))
}
My result is:
0->5(1.0), 1->4(1.0), 1->4(2.0), 0->4(3.0)
0->4(1.0), 1->5(1.0), 1->4(2.0), 0->4(3.0)
Here is what I came up with:
def reduce(accumulator: Seq[MyType], original: Seq[MyType]): Seq[MyType] = original match {
case Nil => accumulator
case head :: xs => {
val found = xs.find(_.timeSpan.getStart().equals(head.timeSpan.getEnd))
if (found.isDefined && found.get.value == head.value) {
reduce(
accumulator :+ (MyType(new Interval(head.timeSpan.getStart, found.get.timeSpan.getEnd), head.value)),
original.diff(Seq(found.get, head))
)
}
else
reduce(
accumulator :+ head,
xs
)
}
}

Iterate over two lists (of differing length) backwards in Scala

What is the most efficient way to iterate over two lists (of differing length) backwards in Scala.
So for two lists
List(a,b,c) and List(1,2)
the pairs would be
(c,2) and (b,1)
Note: I would rather not do a reverse of each list.
A simple way is :
List('a','b','c').reverse zip List(1,2).reverse
Reversing the list is O(n) however, if you're worried about efficiency.
According to List's scaladoc, using reverseIterator might be more efficient. That way you don't creat a new list like with reverse, but traverse it as you keep iterating. That'd be :
val it = list1.reverseIterator zip list2.reverseIterator //returns an Iterator you can force
it.toList // List((c,2), (b,1))
Using parallel collections,
def parRevZip (a: List[String], b: List[Int]) = {
val max = Math.max(a.size, b.size)
val n = Math.abs(a.size - b.size)
if (a.size > b.size)
(max to n+1 by -1).par.map { i => (a(i-1), b(i-n-1)) }
else
(max to n+1 by -1).par.map { i => (a(i-n-1), b(i-1)) }
}
Taking into account different index values for possibly different sized lists, this approach fetches and pairs the same number of elements starting from the end of each list.
Performance needs careful evaluation; for small lists, a plain reverse and zipping may prove much simpler and efficient; for large lists, on the contrary, this parallel approach may be of interest.
Code Refinement
def parRevZip[A,B] (a: List[A], b: List[B]) = {
val aSize = a.size
val bSize = b.size
val max = Math.max(aSize, bSize)
val n = Math.abs(aSize - bSize)
if (aSize > bSize)
(max-1 to n by -1).par.map { i => (a(i), b(i-n)) }
else
(max-1 to n by -1).par.map { i => (a(i-n), b(i)) }
}
Using non recursive collections
Convenient immutable collections here where the computation of size is O(1) (or quasi-constant) (see Recursive collections in Scala such as List) include for instance Array.
Hence,
def parRevZip[A,B] (a: Array[A], b: Array[B])
which does not follow any further the requirement of processing lists.
I think you mean this:
val a = List(1, 2, 3)
val b = List(8, 9)
val result = a.reverse zip b.reverse
Here is my attempt at this problem. The original lists a and b are not duplicated. The operation is O(N) due to List.size:
object test extends App {
val a = List("a", "b", "c") //> a : List[String] = List(a, b, c)
val b = List(1, 2) //> b : List[Int] = List(1, 2)
val aSize = a.size //> aSize : Int = 3
val bSize = b.size //> bSize : Int = 2
// find which is smaller and which is bigger list
val (smaller, bigger) = if (aSize < bSize) (a, b) else (b, a)
//> smaller : List[Any] = List(1, 2)
//| bigger : List[Any] = List(a, b, c)
// skip the extra entries from head of bigger list
val truncated = bigger.drop(Math.abs(aSize - bSize))
//> truncated : List[Any] = List(b, c)
val result = if (a == smaller)
smaller.zip(truncated).reverse
else
truncated.zip(smaller).reverse //> result : List[(Any, Any)] = List((c,2), (b,1))
}

Finding index of row from a list

I am trying to get the index of a row using Scala from a list consisting of lists of integers List[List[Int]]. I already have two functions that given the row index/column index and the grid as parameters, it outputs all the elements in that row. What I need is a function that given a particular element (eg: 0), it finds its row index and column index and puts them in a list: List[(Int, Int)]. I tried to code a function that gives back an index when encountering 0 and then I passed the function to the whole grid. I don't know if I'm doing it the right way. Also, I couldn't figure out how to return the list.
Also, I cannot use any loops.
Thanks in advance.
def Possibilities(): List[Int] = {
def getRowIndex(elem: Int): Int = elem match
{
case 0 => sudoku.grid.indexOf(sudoku.row(elem))
case x => x
}
val result1 = sudoku.grid map {row => row map getRowIndex}
}
I think with two dimensions it is much easier to write such a method with for comprehensions.
Given a List[List[Int]] like this:
val grid = List(
List(1, 2, 3),
List(4, 5, 6),
List(3, 2, 1))
we can simply walk through all the rows and columns, and check whether each element is the one we are looking for:
def possibilities(findElem: Int): List[(Int, Int)] = {
for (
(row, rowIndex) <- grid.zipWithIndex;
(elem, colIndex) <- row.zipWithIndex
if elem == findElem
) yield (rowIndex, colIndex)
}
The yield keyword creates a collection of the results of the for loop. You can find more details on Scala's forloop syntax here (and a more thorough discussion on how this relates to map, flatMap, etc. here).
So, if you don't want to use a for loop, simply 'translate' it into an equivalent expression using map. flatMap, and withFilter:
def possibilities(findElem: Int): List[(Int, Int)] = {
grid.zipWithIndex flatMap { rowAndIndex =>
rowAndIndex._1.zipWithIndex.withFilter(_._1 == findElem) map { colAndIndex =>
(rowAndIndex._2, colAndIndex._2)
}
}
}
Step 1, create a collection of all possible tuples of indices, with a for comprehension (for looks like a loop but it is not)
val tuples = for (i <- grid.indices; j <- grid.head.indices) yield (i, j)
Step 2, filter this collection
tuples.filter { case (i, j) => grid(i)(j) == valueToFind }

How can I compare Lists for equality in Dart?

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;