I have made a list in C# and I want to make a test to see if all the values of the Id fields are unique.
public static List<RestaurantReview> _reviews = new List<RestaurantReview>
{
new RestaurantReview
{
Id = 1,
Name = "McDonalds",
},
new RestaurantReview
{
Id = 2,
Name = "Burger King",
},
}
Because I did some debugging I fount out that it is running trough the list but I do not get the proper test value. Could someone please explain what I am doing wrong here?
[TestMethod()]
public void CheckReviewIds()
{
var FirstReview = ReviewsController._reviews.First();
bool AllUniqueIds = ReviewsController._reviews.All(s => s.Id == FirstReview.Id);
Assert.IsFalse(AllUniqueIds);
}
Thanks in advance.
Another solution that will have better performance in terms of time (specially if the data set that you have is large) is to use a HashSet like this:
bool IsAllUnique<T>(IEnumerable<T> values)
{
HashSet<T> hash_set = new HashSet<T>();
return values.All(x => hash_set.Add(x));
}
And then you can use it like this:
bool unique = IsAllUnique(ReviewsController._reviews.Select(x => x.Id));
This solution depends on the fact that HashSet.Add will return false if the value we are trying to add already exists.
One reason why this solution has better performance is that it does not have to go through the rest of the items in the list if it detects a duplicate item.
You're checking that all values are not equal to the first. This can be the case if the values are for example [1, 2, 3, 3], none are equal to the first but itself, but 3 == 3.
Instead, you can GroupBy to group them by value and then check that they are distinct. I'm assuming performance isn't a big issue here (that's the case if the list is less than 100000 items which I assume):
ReviewsController._reviews.GroupBy(x => x.Id).Count() == ReviewsController._reviews.Count;
Note that it might not be the best idea to test the internal state of a component, instead test the API it is exposing. Otherwise, the contract you define through your unit tests is limited by your implementation detail. This last paragraph is just on man's opinion though.
I find that the easiest way to verify this is to count all the distinct values and compare them with the number of actual values:
var actual = ReviewsController._reviews.Select(r => r.Id).Distinct().Count();
var expected = ReviewsController._reviews.Count();
Assert.Equal(expected, actual);
Related
In one of my tests, I want to ensure that a collection has certain items. Therefore, I want to compare this collection with the items of an expected collection not regarding the order of the items. Currently, my test code looks somewhat like this:
[Fact]
public void SomeTest()
{
// Do something in Arrange and Act phase to obtain a collection
List<int> actual = ...
// Now the important stuff in the Assert phase
var expected = new List<int> { 42, 87, 30 };
Assert.Equal(expected.Count, actual.Count);
foreach (var item in actual)
Assert.True(expected.Contains(item));
}
Is there any easier way to achieve this in xunit.net? I can't use Assert.Equal as this method checks if the order of the items is the same in both collections. I had a look at Assert.Collection but that doesn't remove the Assert.Equal(expected.Count, actual.Count) statement in the code above.
Brad Wilson from xunit.net told me in this Github Issue that one should use LINQ's OrderBy operator and afterwards Assert.Equal to verify that two collections contain equal items without regarding their order. Of course, you would have to have a property on the corresponding item class that you can use for ordering in the first place (which I didn't really have in my case).
Personally, I solved this problem by using FluentAssertions, a library that provides a lot of assertion methods that can be applied in a fluent style. Of course, there are also a lot of methods that you can use to validate collections.
In the context of my question, I would use something like the following code:
[Fact]
public void Foo()
{
var first = new[] { 1, 2, 3 };
var second = new[] { 3, 2, 1 };
first.Should().BeEquivalentTo(second);
}
This test passes because the BeEquivalentTo call ignores the order of the items.
Shouldly is also a good alternative if you do not want to go with FluentAssertions.
Not a Xunit, but a Linq answer :
bool areSame = !expected.Except(actual).Any() && expected.Count == actual.Count;
So in XUnit :
Assert.True(!expected.Except(actual).Any() && expected.Count == actual.Count));
As #robi-y said, in Microsoft.VisualStudio.QualityTools.UnitTestFramework there is CollectionAssert.AreEquivalent
Maybe another way is:
Assert.True(expected.SequenceEqual(actual));
This does checks the order too. This is what happens internally:
using (IEnumerator<TSource> e1 = first.GetEnumerator())
using (IEnumerator<TSource> e2 = second.GetEnumerator())
{
while (e1.MoveNext())
{
if (!(e2.MoveNext() && comparer.Equals(e1.Current, e2.Current))) return false;
}
if (e2.MoveNext()) return false;
}
return true;
So if you don't care about the order, just order both lists before:
Assert.True(expected.OrderBy(i => i).SequenceEqual(actual.OrderBy(i => i)));
If the items in your collection are guaranteed to be unique, you could use a HashSet. That's because a HashSet is unordered by nature.
[Fact]
public void Foo()
{
var expected = new HashSet<int> { 1, 2 ,3 };
var actual = new HashSet<int> { 3, 2, 1 };
Assert.Equal(expected, actual);
}
This works because xUnit uses the ISet.SetEquals() method.
This method ignores the order of elements and any duplicate elements in other.
If the actual collection is just a regular collection (not a HashSet) then you can still use SetEquals() yourself but you must realize that duplicates will be ignored.
[Fact]
public void Foo()
{
var expected = new HashSet<int> { 1, 2 ,3 };
var actual = new [] { 3, 2, 1, 1, 1 };
// This also passes, but may not be what you want
Assert.True(expected.SetEquals(actual));
}
You can use CollectionAssert.AreEquivalent from Microsoft
CollectionAssert.AreEquivalent(expected, actual);
This is almost the same as your code. The only simplification is using Assert.Contains instead of Assert.True(expected.Contains(...)).
[Fact]
public void SomeTest()
{
// Do something in Arrange and Act phase to obtain a collection
List<int> actual = ...
// Now the important stuff in the Assert phase
var expected = new List<int> { 42, 87, 30 };
Assert.Equal(expected.Count, actual.Count);
foreach (var item in expected)
Assert.Contains(item, actual);
}
I have a list of "Issue" objects and i want to sort them by the "Priority" field.
The issue is that "Priority" is a string name like "HIGH", "MEDIUM" so i don't have an Id that i can sort by. How do I sort and tell the sorter that "HIGH" is higher than "MEDIUM" which is higher than "LOW" ?
The obvious way would be:
string[] priorities = { "LOW", "MEDIUM", "HIGH" };
var orderedIssues = issues.OrderByDescending
(issue => Array.IndexOf(priorities, issue.Priority));
But consider using an enumeration:
public enum Priority
{
Low,
Medium,
High
}
var orderedIssues = issues.OrderByDescending
(issue => (Priority)Enum.Parse(typeof(Priority), issue.Priority, true));
Even better would be using the enumeration type as the type of the property / field itself, in which case it's as simple (and less prone to error) as:
var orderedIssues = issues.OrderByDescending(issue => issue.Priority);
The easiest way would probably be like:
private static int MapPriority(string priority)
{
switch(priority.ToUpperInvariant())//skip the case bit if safe
{
case "HIGH":
return 1;
case "MEDIUM":
return 2;
case "LOW":
return 3;
default:
return 4;
}
}
var sorted = someCollection.OrderBy(i => MapPriority(i.PriorityProperty));
With a db-backed form you'll need a function in the DB you can call into. This is in-memory only.
With lots of possible values, I'd base it on a dictionary rather than hand-code. I'd hand-code for three as in this case though (unless the values used could change, a further complication making dictionary-based approaches the only way).
If sorting a serious number of such items, or calling this a lot, I'd go with an IComparer<T> implementation, or have the item itself implement IComparable<T>.
You could, in this specific case, also use Linq's OrderBy method:
var sortedList = issueList.OrderBy(i=>
i.Priority == "HIGH"
? 1
: i.Priority == "MEDIUM"
? 2
: 3).ToList();
As a one-liner this wouldn't be too bad. You could also put the strings into an array, list or Dictionary in the order you want them sorted (or containing the sort order as the Value in the case of the Dictionary).
The one downside of using OrderBy is that it doesn't affect the source List unless you tell it to by reassigning the List to the result. In all cases, it will create two additional collections; an internally-used array or list within OrderBy (sorts have to have knowledge of the entire collection they're sorting) and the List produced by ToList(). So, this will require O(2N) additional memory, while List.Sort() could be in-place (not sure if it actually is, but it does use QuickSort which is normally in-place).
public enum Priority
{
LOW = 1,
MEDIUM = 2,
HIGH = 3
}
issues.OrderByDescending(issue=>issue.Priority);
Something like this:
List<Issue> issues = ...;
var result = issues.OrderBy(x=> x.Priority=="HIGH"?1:x.Priority=="MEDIUM"?2:3);
I have 2 lists and the entities of the those lists have some IDs for instance
Client.ID, where ID is a property of Client anf then I have PopulationClient.ID, where ID is a property of the class PopulationClient. So I have two Lists
TList<Client> clients = clientsHelper.GetAllClients();
TList<PopulationClient> populationClients = populationHelper.GetAllPopulationClients();
So then I have a temp List
TList<Client> temp_list = new TList<Client>();
So the problem i am having is doing this efficiently and correctly. This is what I have tried.. but I am not getting the correct results
foreach(PopulationClient pClients in populationClients)
{
foreach(Client client in clients)
{
if(pClients.ID != client.ID && !InTempList(temp_list, pClients.ID))
{
temp_list.Add(client);
}
}
}
public bool InTempList(TList<Client> c, int id)
{
bool IsInList = false;
foreach(Client client in c)
{
if(client.ID == id)
{
IsInList = true;
}
}
return IsInList;
}
So while I am trying to do it right I can not come up with a good way of doing it, this is not returning the correct data because in my statement in the first loop at the top,at some point one or more is different to the otherone so it adds it anyways. What constraints do you think I should check here so that I only end up with a list of Clients that are in population clients but not in Clients?.
For instance population clients would have 4 clients and Clients 2, those 2 are also in population clients but I need to get a list of population clients not in Clients.
ANy help or pointers would be appreciated.
First, let's concentrate on getting the right results, and then we'll optimize.
Consider your nested loops: you will get too many positives, because in most (pclient, client) pairs the IDs wouldn't match. I think you wanted to code it like this:
foreach(PopulationClient pClients in populationClients)
{
if(!InTempList(clients, pClients.ID) && !InTempList(temp_list, pClients.ID))
{
temp_list.Add(client);
}
}
Now for the efficiency of that code: InTempList uses linear search through lists. This is not efficient - consider using structures that are faster to search, for example, hash sets.
If I understand what you're looking for, here is a way to do it with LINQ...
tempList = populationList.Where(p => !clientList.Any(p2 => p2.ID == p.ID));
Just to offer another LINQ-based answer... I think your intent is to populate tempList based on all the items in 'clients' (returned from GetAllClients) that don't show up (based on 'ID" value) in the populationClients collection.
If that's the case, then I'm going to assume that populationClients is sufficiently large to warrant doing a hash-based looked (if it's less than 10 items, the linear scan may not be a big deal, for instance).
So we want a fast-lookup version of all the ID values from the populationClients collection:
var populationClientIDs = populationClients.Select(pc => pc.ID);
var populationClientIDHash = new HashSet(populationClientIDs);
Now that we have the ID values we want to ignore in a fast lookup data structure, we can then use that as a filter for the clients:
var filteredClients = clients.Where(c => populationClientIDHash.Contains(c.ID) == false);
Based on the usage/need, you could either populate the tempList from 'filteredClients', or do a ToList, or whatever.
I have 2 lists and I would like to remove the items when the items from the first list is not present in the second list.
public class ResolutionsRow
{
public String Name { get; set; }
public int ID { get; set; }
}
public List<ResolutionsRow> Categories { get; set; }
In the following Category.LoadForProject(project.ID) returns an IList
DeleteItems(Category.LoadForProject(project.ID), Categories);
private void DeleteItems(dynamic currentItems, dynamic items)
{
if (currentItems != null)
{
foreach (var existingItem in currentItems)
{
if (items.Contains(existingItem.Name))
items.Remove(existingItem.Name);
}
}
}
I am having the error message
The best overloaded method match for 'System.Collections.Generic.List.Contains(MvcUI.Models.ResolutionsRow)' has some invalid arguments. What is wrong with my code and how can I correct it? Help please.
I have tried to change the code to, but I am having the error message
Error 6 Argument 1: cannot convert from 'int' to API.Category' MvcUI\Models\ProjectModel.cs 255 44 MvcUI
Error 5 The best overloaded method match for 'System.Collections.Generic.ICollection.Contains(API.Category)' has some invalid arguments MvcUI\Models\ProjectModel.cs 255 24 MvcUI
var categories = Category.LoadForProject(project.ID);
foreach (var item in Categories)
{
if(categories.Contains(item.ID))
{
}
}
Here's the easy LINQ answer:
var currentItems = new int[] { 1, 2, 5, 6 };
var items = new int[] { 2, 3, 4, 5 };
var resultItems = items.Except(currentItems); // resultItems == new int[] { 3, 4 }
What is items? I'm guess it is the list of ResolutionsRow - so you will need to search for an item with that name/id, not the name/id itself.
If they are the same object instances, then just Remove(existingItem) will work, but otherwise (if they are different object instances that happen to have the same .Name):
items.RemoveAll(item => item.Name == existingItem.Name);
by the way; do you really need dynamic here? Without it, the compiler would tell you the problem. It isn't helping you any, and could well cause a lot of problems (explicit interface implementations, lambdas, etc - there are constructs that aren't fans of dynamic)
Change
items.Contains(existingItem.Name);
and
items.Remove(existingItem.Name);
to
items.Contains(existingItem);
items.Remove(existingItem);
you want to use items1.Except(items2)
Your items.Contains method signature is expecting a type different than what you provided. Looks like you are providing a string instead of a ResolutionsRow.
How often are you doing this, and with how many items in each list? What you are doing is commonly considered a "Set operation"(union, intersection, minus, etc). If the answer to either of the previous questions is "a lot", then you want to consider using a SortedSet or HashSet.
Your current implementation is O(m*n) (where m and n are the sizes of the two lists). If you use a hash set it is O(n), since only the second set is actually iterated over. There is also a cost to build the sets (O(m+n)), but if you have enough objects or can use it for more than just one operation it can be worth it.
I have a list of objects. These objects have three variables, ID, Name, & value. There can be a lot of objects in this list, and I need to find one based on the ID or Name, and change the value.
Example
class objec
{
public string Name;
public int UID;
public string value;
}
List<objec> TextPool = new List<objec>();
How would I find the one entry in TextPool that had the Name of 'test' and change its value to 'Value'.
The real program has many more search options, and values that need changing, so I couldn't just use a Dictionary (though Name and UID or unique identifiers).
Any help would be great
You could use LINQ to find it, then change the element directly:
var item = TextPool.FirstOrDefault(o => o.Name == "test");
if (item != null)
item.value = "Value";
If you wanted to change all elements that match, you could, potentially, even do:
TextPool.Where(o => o.Name == "test").ToList().ForEach(o => o.value = "Value");
However, I personally would rather split it up, as I feel the second option is less maintainable (doing operations which cause side effects directly on the query result "smells" to me)...
var find = TextPool.FirstOrDefault(x => x.Name == "test");
if (find != null)
{
find.Name = "Value";
}
Sounds like a job for LINQ!
var matchedObject =
from t in TextPool
where t.UName == "test"
select t;
This is assuming your search is defined in code. If your code is driven by the UI, you may simply need to do a linear iteration. To search all possible attributes, without indexing, it isn't going to get any faster.
[ Edit: Was beaten to the punch, but leaving this up as an example of a different syntax, plus a link ]
List<objec> TextPool = new List<objec>();
objec found = TextPool.FirstOrDefault(item => item.Name == "test");
if (found != null) found.value = "Value";
If you are going to perform many lookups, you could cache the results in multiple Dictionary<> instances (or Lookup<> instance if keys are not unique).