Fluent assertions and comparison of List<List<string>> - c#

I have a problem using Fluent Assertions to compare two collections of type List<List<string>>. When using the Should().Equal() method (order is important) I get the following (cryptic ;-) message:
Expected collection to be equal to {{"Telefoonnummer"}, {"E-mailadres"}, {"E-mailadres controle"}}, but {{"Telefoonnummer"}, {"E-mailadres"}, {"E-mailadres controle"}} differs at index 0.
So, the objects appear to be equal. Also when debugging the objects appear to be exactly the same. When comparing two List<string> objects the test passes, no problems, but the same test with List<List<string>> fails. Am I using the wrong assertion method? Or does Fluent Assertions not handle this type of collection correctly?

Instead of Should().Equal() use actualList.ShouldBeEquivalentTo(expectedList. config => config.WithStrictOrder());
ShouldBeEquivalentTo method will check that both lists contains items with same values. In cases when list contains instance of reference types, it will compare the full object graph of those instances. Adding as a second parameter
config => config.WithStrictOrdering() will check that order of items is same as in expected list.
Should().Equal() on other hand will use "standard" equality check, where var listOne = new List<string> { "test" }; and var listTwo = new List<string> { "test" }; will be different instances.

While comparing a string using == checks value equality, List<string> checks the address of the list. This means two lists, containing the same elements are not the same because you are comparing the addresses of the lists instead of the items inside. Lets make an example:
List<string> listA = new List<string> { "item" };
List<string> listB = new List<string> { "item" };
bool equal = listA == listB; //equal will be false
To solve your problem you could combine SelectMany and SequenceEqual to compare the items inside the lists. Here is a small example:
List<List<string>> listToCompare = new List<List<string>>()
{
new List<string> {"first", "second"},
new List<string> {"third"}
};
List<string> expectedList = new List<string> { "first", "second", "third" };
bool sequenceEqual = listToCompare.SelectMany(i => i).SequenceEqual(expectedList); //sequenceEqual will be true

Related

.net core list removeall() method is strange

It seems very simple, but this error happens.
IList<string> list = new List<string>();
list.Add("001");
list.Add("002");
list.Add("003");
list.Add("004");
list.ToList().RemoveAll(s => s == "002");
return list.Count.ToString();
The list Count should be 3, but it will still be 4. Is a bug in RemoveAll() method? If using List rathan than IList declaration, it works well.
Edit
1. If not using ToList() method, there is no RemoveAll() method to call.
How can I avoid this situation to use IList as a argument, list firstly is a reference type. Should I not use IList as a declaration totally? We have used IList everythere in our project.
public string List()
{
IList<string> list = new List<string>();
list.Add("001");
list.Add("002");
list.Add("003");
list.Add("004");
Remove(list);
return list.Count.ToString();
}
private void Remove(IList<string> list)
{
list.ToList().RemoveAll(a => a == "002");
}
If there is a new method to support, it will be better. Thanks everyone here.
When you're using ToList you actually create a shallow copy of the list, which then you apply the RemoveAll on the new list, thus, the original list doesn't been affected.
If you only looking for the count of none-"002" items, then simple count will suffice
list.Count - list.Count(i => i == "002");
Otherwise if you actually want to remove those items from the original list, then you will need to solve it the old fashion way, using for loop.
Anyway, if IList is not that important, you can save the list as List and not IList, and use RemoveAll method.
ToList() returns a shallow copy of the original list (ie, it's a new instance and modifications made to it will not be reflected back in list).
Since you need to use the RemoveAll method, I recommend storing the variable as List instead of IList:
List<string> list = new List<string>();
list.Add("001");
list.Add("002");
list.Add("003");
list.Add("004");
list.RemoveAll(s => s == "002");
Console.WriteLine(list.Count()); // 3
You can also achieve the same thing using LINQ operators:
List<string> list = new List<string>();
list.Add("001");
list.Add("002");
list.Add("003");
list.Add("004");
list = list.Where(s => s != "002").ToList();
Console.WriteLine(list.Count());
More succinctly:
List<string> list = new List<string>(new[] {
"001",
"002",
"003",
"004"
});
// or
//List<string> list = new[] {
// "001",
// "002",
// "003",
// "004"
//}.ToList();
Console.WriteLine(list.Count(s => s != "002")); // 3

Collect indexes from list

Is there a linq function in c# which enables you to collect IEnumerables from a specific range of indexes?
An example would be
var objectArray = new string[] { "Bill", "Bob", "Joe", "Phil", "Tom", "Paul" };
var indexArray = new int[] { 1, 3, 5 };
var list = objectArray.Where(SOME_FUNCTION_TO_GET_INDEXES ??).ToList();
//output would be list:
//Bob
//Phil
//Paul
Just use Select with your indexArray and return the item from objectArray via indexing.
var list = indexArray.Select(i => objectArray[i]);
Note that this works very efficiently for any collection that allows indexing (for example, Array and List<T>). In the more general case of having an IEnumerable or ICollection, you wouldn't be able to index directly. In which case you'd need to see Jon's answer. Depending on the sizes of the lists involved, and how many items you need to look up, it might be worth converting your IEnumerable to an Array or List (using ToArray for example) first.
If the original datasource is already accessible by index, such as for a list or an array, you can just use indexArray.Select as Matt showed.
If you've got an IEnumerable<T> instead, you can use the Where overload which provides the index as well as the value. So:
var list = objectArray.Where((value, index) => indexArray.Contains(index))
.ToList();

Convert 'ArrayList' to 'List<string>' (or 'List<T>') using LINQ

I want to convert an ArrayList to a List<string> using LINQ. I tried ToList() but that approach is not working:
ArrayList resultsObjects = new ArrayList();
List<string> results = resultsObjects.ToList<string>();
Your code actually shows a List<ArrayList> rather than a single ArrayList. If you're really got just one ArrayList, you'd probably want:
ArrayList resultObjects = ...;
List<string> results = resultObjects.Cast<string>()
.ToList();
The Cast call is required because ArrayList is weakly typed - it only implements IEnumerable, not IEnumerable<T>. Almost all the LINQ operators in LINQ to Objects are based on IEnumerable<T>.
That's assuming the values within the ArrayList really are strings. If they're not, you'll need to give us more information about how you want each item to be converted to a string.
I assume your first line was meant to be ArrayList resultsObjects = new ArrayList();.
If the objects inside the ArrayList are of a specific type, you can use the Cast<Type> extension method:
List<string> results = resultsObjects.Cast<string>().ToList();
If there are arbitrary objects in ArrayList which you want to convert to strings, you can use this:
List<string> results = resultsObjects.Cast<object>().Select(x => x.ToString())
.ToList();
You can also use LINQ's OfType<> method, depending on whether you want to raise an exception if one of the items in your arrayList is not castable to the desired type. If your arrayList has objects in it that aren't strings, OfType() will ignore them.
var oldSchoolArrayList = new ArrayList() { "Me", "You", 1.37m };
var strings = oldSchoolArrayList.OfType<string>().ToList();
foreach (var s in strings)
Console.WriteLine(s);
Output:
Me
You
You can convert ArrayList elements to object[] array using ArrayList.ToArray() method.
List<ArrayList> resultsObjects = new List<ArrayList>();
resultsObjects.Add(new ArrayList() { 10, "BB", 20 });
resultsObjects.Add(new ArrayList() { "PP", "QQ" });
var list = (from arList in resultsObjects
from sr in arList.ToArray()
where sr is string
select sr.ToString()).ToList();

Whats the difference between these three ways of creating a new List<string> in C#?

Whats the difference between these three ways of creating a new List<string> in C#?
A = new List<string>();
B = new List<string> { };
C = new List<string>() { };
There is no difference because you are not initializing anything inside the list at the time of declaring it.
If you were to add some strings at declaration-time, you would need to go with the second or third choice:
var B = new List<string> { "some", "strings" };
var C = new List<string>() { "some", "strings" };
The third option would only be necessary if you were to pass a value into the List<of T> constructor:
var C = new List<string>(5) { "some", "strings" };
or
var C = new List<string>(5); // empty list but capacity initialized with 5.
There are more constructors available for the List<of T> class as well (e.g. passing an existing collection or IEnumerable into the List constructor). See MSDN for details.
Those are equivalent, all three create an empty List<string>.
is a simple, parameterless constructor
uses the collection initialization syntax, where you are allowed to omit the braces of the constructor if the class provides a parameterless constructor
the same as 2., just providing the optional constructor braces.
The collection initialization syntax allows you to provide data while constructing the object,
List<string> list = new List<string> { "one", "two", "three" };
gets expanded to
List<string> list = new List<string>();
list.Add("one");
list.Add("two");
list.Add("three");
by the compiler.
Those are all identical. There's no difference in the end result, just in the syntax.
In your example, all three statements do the same: they create an empty string list. Statement B and C can assign some initial data to the list, e.g.
ingredients = new List<string> { "Milk", "Sugar", "Flour" };
The () as in statement C can omitted then.
{} is used for collection\object initilizer.
If it remains blank it doesn't do a thing.
So all those lines produces the same thing.
In the example you gave to us, the result will be the same.
In terms of performance you will not find any problem using any of that options for initializing a empty list, because the IL generated will one contain the ctor(), as you can see using the IL DASM.
If you want to initialize the list with some information, you can go for the option 2 or 3.
List<string> b = new List<string> { "abc", "abc" };
List<string> c = new List<string>() { "abc", "abc"};
In terms of performance is also the same, the IL generated will be exactly the same, containing the ctor() and two Add for both.
But you should use the best one to read. To avoid any problem reading the code for you colleagues, I would go for
List<string> a = new List<string>();
But that is a question of personal opinion.

Using Linq intersect with sub values?

I found this post Match elements between 2 collections with Linq in c# which explained how you can use Intersect to find matching elements between two lists.
Can you use this to match elements in two lists that are not of exactly the same, but have "sub values" that you want to match?
My example is this: I have two collections, each containing lists of XElements. The one with elements called <link> and the other with elements called <file>, each have attributes called "path", and it is this attribute I want to match. If the path attribute is equal, I want a match.
In the result set I would like a list of all the elements whose paths match the paths of the elements.
How can this be done?
I would suggest to use LambdaComparer which can be passed into the Intersect() method as Equality Comparer, it allows specifying comparison logic in place by providing boolean condition instead introducing a new comparer class each time, so your code would be clear enough:
firstCollection.Intersect(
secondCollection,
new LambdaComparer<YourClass>(
(item1, item2) => item1.PropertyName == item2.PropertyName));
// Below are lists and User class which demonstrates LambdaComparer and Intersect()
public class User
{
public string Name { get; set; }
}
IList<User> list1 = new List<User>
{
new User {Name = "A"},
new User { Name = "B"}
};
List<User> list2 = new List<User>
{
new User {Name = "C"},
new User { Name = "B"}
};
var resultSet = list1.Intersect<User>(
list2,
new LambdaComparer<User>((item1, item2) => item1.Name == item2.Name));
Basically if you need to compare cusotm attributes, you still can encapsulate this logic into
Func<User, User, bool> userNameComparer = (user1, user2) =>
{
// check attributes using user1.GetType().GetCustomAttributes()
};
And then use this comparer funciton as:
var resultSet = list1.Intersect<User>(
list2,
new LambdaComparer<User>((item1, item2) => userNameComparer));
EDIT: Note ragarding particular impelemntaion referenced in this answer
There is could be a problem that by default for hash funciton is hardcoded 0
6 public LambdaComparer(Func<T, T, bool> lambdaComparer) :
7 this(lambdaComparer, o => 0)
8 {
9 }
This can lead to performance issues in some cases so I would recommend to refactor it as:
public LambdaComparer(Func<T, T, bool> lambdaComparer) :
this(lambdaComparer,
EqualityComparer<T>.Default.GetHashCode(o))
{
}
So it wil use built in GetHashCode() implementation

Categories