I'm new to unit testing, and I wanted to test a method that gets a string array that contains some names like "John,Doe" and then it splits the name by the "," and populates a list of PersonModel with those names, so I expect that there is a PersonModel with first name John, and last name Doe, in the returned list, but the Assert.Contains method keeps throwing this error:
Assert.Contains() Failure
Not found: PersonModel { FirstName = "John", FullName = "John Doe",
LastName = "Doe" }
In value: List [PersonModel {
FirstName = "John", FullName = "John Doe", LastName = "Doe" },
PersonModel { FirstName = "Jane", FullName = "Jane Doe", LastName =
"Doe" }]
This is the method for converting names to people list in the DataAccess class:
public static List<PersonModel> ConvertCsvNamesToPeopleList(string[] csvContent)
{
List<PersonModel> output = new List<PersonModel>();
foreach (string line in csvContent)
{
string[] data = line.Split(',');
output.Add(new PersonModel { FirstName = data[0], LastName = data[1] });
}
return output;
}
And this is the test:
[Fact]
public void ConvertCsvNamesToPeopleList_ValidCsvContent_ShouldWork()
{
string[] csvContent = { "John,Doe", "Jane,Doe" };
var expectedPerson = new PersonModel { FirstName = "John", LastName = "Doe" };
var expectedPerson2 = new PersonModel { FirstName = "Jane", LastName = "Doe" };
var actual = DataAccess.ConvertCsvNamesToPeopleList(csvContent);
Assert.Contains(expectedPerson, actual);
Assert.Contains(expectedPerson2, actual);
}
The person model:
public class PersonModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string FullName => $"{ FirstName } { LastName }";
}
Contains relies on finding two objects that are equal, so you should override its Equals method. For good measures, you should override its GetHashCode method too, so you can use it as a key of a dictionary if you ever need to:
public override bool Equals(object obj)
{
return obj is PersonModel model &&
FirstName == model.FirstName &&
LastName == model.LastName;
}
public override int GetHashCode()
{
return HashCode.Combine(FirstName, LastName);
}
Related
I have the two lists below where one comes from the database and the other from a JSON. The one from the database has it's Id associated.
List from database:
EmployeeId FirstName LastName EmployeeNumber
1234 Tom Cruise 98372829
5555 James Bond 93932228
The employeeId is a GUID Saved in the database.
Now I retrieve a list of Employees again to detect changes - James Bond Lastname changed. And I used automapper to map in the same format as my database entity.
List from JSON:
EmployeeId FirstName LastName EmployeeNumber
000-0000... Tom Cruise 98372829
000-0000... James Carter 93932228
Now I want to update the first list with the FirstName and LastName based on the EmployeeNumber.
// Employees retrieved in JSON
var retrievedEmployees = JsonSerializer.Deserialize<List<EmployeeDto>>(methodToRetrieveEmployees()))!.ToList();
var mappedEmployees = _mapper.Map<IEnumerable<Employee>>(retrievedEmployees);
var existingEmployeeFromDatabase = await GetExistingEmployees();
var employeesWithLatestUpdates = mappedEmployees
.Where(y => existingEmployeeFromDatabase.Any(z => z.Number == y.Number)).ToList();
So What I need to do is to update employeeswithLatestChanges (Id,FirstName and LastName) with the values from existingEmployees from the database. Since they don't have Id, this should be mapped by the EmployeeNumber.
I have tried to use Union/joins but no luck.
Updating by linq in c# 6
var updatedEmployee = employeeswithLatestChanges.Select(x => new Employee
{
FirstName = existingEmployees.FirstOrDefault(y => y.EmployeeId == x.EmployeeNumber)?.FirstName?? x.FirstName,
LastName = existingEmployees.FirstOrDefault(y => y.LastName == x.code)?.LastName ?? x.LastName ,
});
Can Use Loop also
foreach (var dbEmp in existingEmployees)
{
foreach(var emp in (employeeswithLatestChanges.Where(t => t.EmployeeNumber == dbEmp.EmployeeId)))
{
emp.FirstName= dbEmp.FirstName;
emp.LastName= dbEmp.LastName;
}
}
To fix the idea we can assume the following class to represent an employee:
public sealed class Employee
{
public string FirstName { get; set; }
public string LastName { get; set; }
public Guid Id { get; set; }
public int Number { get; set; }
}
Suppose you have a collection of employees named employees that you want to update by using another collection of employees named updatedEmployees:
IEnumerable<Employee> employees = ....;
IEnumerable<Employee> updatedEmployees = ....;
The simplest way I can think of to solve your problem is the following:
public static void Main(string[] args)
{
// previous code omitted for brevity
Dictionary<int, Employee> employeeNumberToUpdatedEmployee = updatedEmployees.ToDictionary(x => x.Number);
foreach (var employee in employees)
{
if (employeeNumberToUpdatedEmployee.TryGetValue(employee.Number, out var updatedEmployee)
{
employee.FirstName = updatedEmployee.FirstName;
employee.LastName = updatedEmployee.LastName;
}
}
// subsequent code omitted for brevity
}
An alternative way to solve this problem is to perform a join operation by using LINQ to objects, as in the following code:
var employees = new List<Employee>
{
new Employee{ Id = Guid.NewGuid(), Number = 11, FirstName = "Bob", LastName = "Red" },
new Employee{ Id = Guid.NewGuid(), Number = 13, FirstName = "Alice", LastName = "Smith" },
new Employee{ Id = Guid.NewGuid(), Number = 5, FirstName = "Max", LastName = "Brown" },
};
var updatedEmployees = new List<Employee>
{
new Employee{ Id = Guid.NewGuid(), Number = 11, FirstName = "Bob", LastName = "Verdi" },
new Employee{ Id = Guid.NewGuid(), Number = 13, FirstName = "Alice", LastName = "Rossi" },
new Employee{ Id = Guid.NewGuid(), Number = 78, FirstName = "Sam", LastName = "Smith" },
};
// here we are using the fact that we can have, at most, one match
var query = from employee in employees
join updatedEmployee in updatedEmployees on employee.Number equals updatedEmployee.Number into matches
from match in matches.DefaultIfEmpty()
select new Employee
{
Id = employee.Id,
Number = employee.Number,
FirstName = match?.FirstName ?? employee.FirstName,
LastName = match?.LastName ?? employee.LastName,
};
foreach (var item in query)
{
Console.WriteLine($"{item.FirstName} {item.LastName}");
}
I have an array of objects similar to this:
class StateVisitor
{
string FirstName { get; set; }
string LastName { get; set; }
string StateViseted { get; set; }
}
StateVisitor[] StateVisitors = {
new() { FirstName = "Bob", LastName = "Smith", StateViseted = "AL" },
new() { FirstName = "Bob", LastName = "Smith", StateViseted = "AK" },
new() { FirstName = "Bob", LastName = "Jones", StateViseted = "AL" },
new() { FirstName = "Sam", LastName = "Smith", StateViseted = "UT" }
}
And I want to do something like this:
class VisitorsWithCombinedStates {
string FirstName { get; set; }
string LastName { get; set; }
string[] StatesVisetedArray { get; set; }
}
VisitorsWithCombinedStates[] visitorsWithCombinedStates = StateVisitors... /* Linq magic? */
visitorsWithCombinedStates.ForEach(v
=> Console.WriteLine($"{v.FirstName} {v.LastName} visited {string.Join(", ",v.StatesVisitedArray)}"));
// "Bob Smith visited AL, AK"
// "Bob Jones visited AL"
// "Sam Smith visited UT"
Is there an easy way, in C# (probably with LINQ) to flatten that first array into the second array, where it makes an array of the states visited?
I think you are looking for GroupBy then projecting the groups into your new class:
var visitorsWithCombinedStates = StateVisitors.GroupBy(sv => new { sv.FirstName, sv.LastName }, sv => sv.StateVisited)
.Select(svg => new VisitorsWithCombinedStates {
FirstName = svg.Key.FirstName,
LastName = svg.Key.LastName,
StatesVisitedArray = svg.ToArray()
})
.ToArray();
NOTE: Corrected spelling of visited
You want to group your items by a combination of first and last name and project the result of the grouping as just the state visited
var groups = stateVisitors.GroupBy(sv => new
{
sv.FirstName,
sv.LastName,
},
sv => sv.StateVisited );
Output:
foreach(var g in groups)
{
// g.Key.FirstName
// g.Key.LastName
// g is IEnumerable<string> of visited states
}
An example, to fill your VisitorsWithCombinedStates class:
VisitorsWithCombinedStates[] visitorsWithCombinedStates = StateVisitors.GroupBy(x => new { x.FirstName, x.LastName},x => x.StateViseted)
.Select(x => new VisitorsWithCombinedStates { FirstName = x.Key.FirstName, LastName = x.Key.LastName, StatesVisetedArray = x.ToArray() }).ToArray();
This question already has answers here:
C# - code to order by a property using the property name as a string [duplicate]
(10 answers)
Closed 4 years ago.
I have a list of ListUser class objects. I need to be able to pass in a String value and order by that column in ascending or descending order using text expression. Everything I have seen that uses Lambda expressions, has the object property as a strongly typed value, How can I achieve this by adding in "firstname descending" as a parameter ?
The code is as follows
namespace SortLists
{
class ListUser
{
public int id { get; set; }
public string firstname { get; set; }
public string lastname { get; set; }
public string company { get; set; }
public string phonenumber { get; set; }
}
class Program
{
static void Main(string[] args)
{
var user1 = new ListUser() { id = 1, firstname = "James", lastname = "Smith", company = "Code Logic", phonenumber = "01235 566 456" };
var user2 = new ListUser() { id = 1, firstname = "Chris", lastname = "Andrews", company = "Adobe", phonenumber = "01235 566 456" };
var user3 = new ListUser() { id = 1, firstname = "Paul", lastname = "Jones", company = "Microsoft", phonenumber = "01235 566 456" };
var user4 = new ListUser() { id = 1, firstname = "Peter", lastname = "Williams", company = "Apple", phonenumber = "01235 566 456" };
List<ListUser> users = new List<ListUser>()
{
user1, user2, user3, user4
};
}
}
Add reference to nuget package:
https://www.nuget.org/packages/System.Linq.Dynamic/
Add using System.Linq.Dynamic; at the top.
Use var usersSorted = users.AsQueryable().OrderBy("firstname ASC").ToList();
It's easy with a dictionary. Just start with this:
var sortBy = new Dictionary<string, Func<IEnumerable<ListUser>, IEnumerable<ListUser>>>()
{
{ "firstname", lus => lus.OrderBy(lu => lu.firstname) },
{ "lastname", lus => lus.OrderBy(lu => lu.lastname) },
{ "company", lus => lus.OrderBy(lu => lu.company) },
{ "phonenumber", lus => lus.OrderBy(lu => lu.phonenumber) },
};
Then you can easily sort like this:
List<ListUser> sorted = sortBy["firstname"](users).ToList();
If you want it descending just do this:
List<ListUser> sorted = sortBy["firstname"](users).Reverse().ToList();
Just structure your sort method like so:
if(stringPassed == "firstname")
{
List<ListUser> sortedListUser = listUser.OrderBy(p=>p.firstName).ToList();
}
else if(...) // and so on
if you want to order them by desc order just use LINQ's .OrderByDescending method.
The other cool approach may be that you set your properties to be objects with
string value;
string name;
and loop your input string with reflection towards the properties in your class and get the one you want and order it. It's a fancy way to impress your teacher xaxa.
I'm trying to create a wrapper for selecting multiple items from a single array. I get the result at the end of the code below. Not sure what I'm doing wrong.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Tester.cs
{
class Program
{
static void Main(string[] args)
{
var customers = new[]
{
new { CustomerID = 1, FirstName = "Orlando", LastName = "Gee",
CompanyName = "A Bike Store" },
new { CustomerID = 2, FirstName = "Keith", LastName = "Harris",
CompanyName = "Bike World" },
new { CustomerID = 3, FirstName = "Donna", LastName = "Carreras",
CompanyName = "A Bike Store" },
new { CustomerID = 4, FirstName = "Janet", LastName = "Gates",
CompanyName = "Fitness Hotel" },
new { CustomerID = 5, FirstName = "Lucy", LastName = "Harrington",
CompanyName = "Grand Industries" },
new { CustomerID = 6, FirstName = "David", LastName = "Liu",
CompanyName = "Bike World" },
new { CustomerID = 7, FirstName = "Donald", LastName = "Blanton",
CompanyName = "Grand Industries" },
new { CustomerID = 8, FirstName = "Jackie", LastName = "Blackwell",
CompanyName = "Fitness Hotel" },
new { CustomerID = 9, FirstName = "Elsa", LastName = "Leavitt",
CompanyName = "Grand Industries" },
new { CustomerID = 10, FirstName = "Eric", LastName = "Lang",
CompanyName = "Distant Inn" }
};
var addresses = new[] {
new { CompanyName = "A Bike Store", City = "New York", Country = "United States"},
new { CompanyName = "Bike World", City = "Chicago", Country = "United States"},
new { CompanyName = "Fitness Hotel", City = "Ottawa", Country = "Canada"},
new { CompanyName = "Grand Industries", City = "London", Country = "United Kingdom"},
new { CompanyName = "Distant Inn", City = "Tetbury", Country = "United Kingdom"}
};
IEnumerable<Names> customerfullName = customers.Select(data => new Names {
FirstName = data.FirstName,
LastName = data.LastName});
foreach (Names entry in customerfullName)
{
Console.WriteLine(entry);
}
}
}
class Names
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
}
Tester.cs.Names is what i get repeated when I run the program.
Console.WriteLine uses the ToString method of the object class. By default, that displays the name of the class.
This method is overridden by classes derived from object to display whatever they want. You have not overridden it, so you get the default.
You can reproduce your problem, without LINQ, as follows:
class Names
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
var name = new Names {FirstName = "John", LastName = "Saunders"};
Console.WriteLine(name); // Will display "Tester.cs.Names"
default the ToString will be used, use:
class Names
{
public string FirstName { get; set; }
public string LastName { get; set; }
public override string ToString()
{
return string.Format("{0} {1}", FirstName, LastName);
}
}
It's also possible to create an extra property for the fullname
class Names
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string FullName
{
get
{
return string.Format("{0} {1}", FirstName, LastName);
}
}
}
usage:
foreach (Names entry in customerfullName)
{
Console.WriteLine(entry.FullName);
}
Your Names class has not overridden the ToString method, so it is using the default implementation from object and printing out it's type name. You either need to override ToString in Names to print out the underlying strings, or you need to print out the individual string properties in your foreach loop.
Lets say I have a class called Person:
public class Person
{
public int Age { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
And a list of persons:
Person { Age = 20, FirstName = "John", LastName = "Joe" }
Person { Age = 20, FirstName = "John", LastName = "Joe" }
Person { Age = 10, FirstName = "James", LastName = "Dokes" }
What I want to have is a (new or old with a new property) list that groups the person by age, first name and last name AND I also want to know how many times that object has been grouped.
So the result of the above would be:
Person { Age = 20, FirstName = "John", LastName = "Joe", Count = 2 }
Person { Age = 10, FirstName = "James", LastName = "Dokes", Count = 1 }
Simple:
people.GroupBy(x => new { x.Age, x.FirstName, x.LastName })
.Select(x => new { x.Key.Age, x.Key.FirstName, x.Key.LastName, Count = x.Count() });
Just before I posted I saw answer already from JustAnotherUserYouMayKnow, so was going to cancel, but am posting anyway just to show same result using only the GroupBy, with the resultSelector parameter (instead of separate projection)
var personList = new List<Person> {
new Person { Age = 20, FirstName = "John", LastName = "Joe" },
new Person { Age = 20, FirstName = "John", LastName = "Joe" },
new Person { Age = 10, FirstName = "James", LastName = "Dokes"}};
var results = personList.GroupBy(per => new { per.Age, per.FirstName, per.LastName },
(key, items) => new { key.Age, key.FirstName, key.LastName, Count = items.Count() } );
Non-proc alternative:
List<Person> ls = new List<Person>();
ls.Add (new Person() { Age = 20, FirstName = "John", LastName = "Joe" });
ls.Add(new Person() { Age = 20, FirstName = "John", LastName = "Joe" });
ls.Add(new Person() { Age = 10, FirstName = "James", LastName = "Dokes" });
var result = (from cur in ls
group cur by new { Age = cur.Age, Name = cur.FirstName, LastName = cur.LastName }
into tmpResult
select new { tmpResult.Key.Age,tmpResult.Key.LastName,tmpResult.Key.Name,Count = tmpResult.Count()});