How to Display the Members of a Class - c#

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.

Related

Is there an easy way to flatten an array of objects while making an array of the one column that would be different?

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();

Assert.Contains doesn't find the object in a list

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

C# LINQ Return Generic List<T>

Below are two classes I have sampled.
Without using tuple;
  I would like to send queries directly from the first list to the second list of results.
The part that fails in the encoding appears as convert operations.
I thank you for your time and reply.
static void Main(string[] args)
{
List<liste> personel = new List<liste>{
new liste { PersonId = 1, Name = "Burak", Surname = "Şenyurt", City = "İstanbul", Salary = 890 },
new liste { PersonId = 2, Name = "Maykıl", Surname = "Cordın", City = "Chicago", Salary = 930 },
new liste { PersonId = 3, Name = "Şakiyıl", Surname = "Oniyıl", City = "Los Angles", Salary = 986 },
new liste { PersonId = 4, Name = "Ümit", Surname = "Oniyıl", City = "Los Angles", Salary = 1035 },
new liste { PersonId = 5, Name = "Mehmet", Surname = "Zaferoğlu", City = "Los Angles", Salary = 1265 },
new liste { PersonId = 6, Name = "Hasan", Surname = "Orkun", City = "Los Angles", Salary = 1435 },
new liste { PersonId = 7, Name = "Raşit", Surname = "Mesut", City = "Los Angles", Salary = 1469 },
new liste { PersonId = 8, Name = "Hamdi", Surname = "Tanpınar", City = "Los Angles", Salary = 1535 },
new liste { PersonId = 9, Name = "Şevki", Surname = "Çapkın", City = "Los Angles", Salary = 1636 },
new liste { PersonId = 10, Name = "Özhun", Surname = "Bozkurt", City = "Los Angles", Salary = 1839 }
};
double resAVG = personel.Select(x => x.Salary).Average();
List<Sonuc> reportResult = GetReport(personel,resAVG);
}
Static Method
public static List<Sonuc> GetReport(List<liste> listePersonel , double resAVG)
{
List<Sonuc> result = (from e in listePersonel
where e.Salary >= resAVG
orderby e.Salary descending
//select new Tuple<string, string, double>(e.Name, e.Surname, e.Salary)).ToList<Tuple<string, string, double>>();
select new List<Sonuc>(e.Name, e.Surname, e.Salary)).ToList<Sonuc>(result.ToList());
return result;
}
General Class
public class liste
{
public int PersonId { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public string City { get; set; }
public double Salary { get; set; }
public override string ToString()
{
return $"PersonId : {PersonId}\t\tName , Surname {Name} , {Surname}\t\t\tSalary : {Salary}";
}
}
Result Class
public class Sonuc
{
public string Name { get; set; }
public string Surname { get; set; }
public double Salary { get; set; }
public Sonuc(string Name , string Surname, double Salary)
{
this.Name = Name;
this.Surname = Surname;
this.Salary = Salary;
}
public override string ToString()
{
return $"Name, SurName : {this.Name} , {this.Surname}\t\t\tSalary : {this.Salary}";
}
}
You're trying to construct an instance of List<T> by passing it a string, a string, and a double. List<T> does not have a constructor which takes these parameters. Also, you can't use result before you've assigned it.
Instead, you should project each item in listePersonel to a single instance of Sounc, then take that enumerable to a List<Sounc>.
public static List<Sonuc> GetReport(List<liste> listePersonel , double resAVG)
{
List<Sonuc> result = (from e in listePersonel
where e.Salary >= resAVG
orderby e.Salary descending
select new Sonuc(e.Name, e.Surname, e.Salary)).ToList();
return result;
}

Join 2 collection list together from 2 data sources find the matches and loop through the results

I am wanting to merge together 2 different collections.
Example collection 1: (linqpad to live sql server)
Sample Data (collection azedIdentity):
PersonID | FirstName | LastName |
3197908 John Smith
4444 Jody Smith
55555 Jon Smither
var azedIdentity = PersonMatchNoDOBRequired("John", "Smith").AsDynamic()
.Select (x => new FindPersonContactViewModel
{
PersonID = x.PersonID,
AZEDID = x.AZEDID,
FirstName = x.FirstName,
MiddleName = x.MiddleName,
LastName = x.LastName,
}).ToList();
Now is where I will query a another data source ( in memory for this question)
var personContactRoles = new List<FindPersonContactViewModel>()
{ new FindPersonContactViewModel { PersonID = 3197908, FirstName = "John", MiddleName= "", LastName="Smith", ContactType="Farmer", ContactRoleTypeId = 1, ExistInContactManager = true, ActionType = true, IsInContactManager = true },
new FindPersonContactViewModel { PersonID = 3197908, FirstName = "John", MiddleName= "", LastName="Smith", ContactType="Plumber", ContactRoleTypeId = 2, ExistInContactManager = true, ActionType = true, IsInContactManager = true },
new FindPersonContactViewModel { PersonID = 3197908, FirstName = "John", MiddleName= "", LastName="Smith", ContactType="Landscaper", ContactRoleTypeId = 3, ExistInContactManager = true, ActionType = true, IsInContactManager = true },
new FindPersonContactViewModel { PersonID = 2, FirstName = "Jon", MiddleName= "", LastName="Smither" },
new FindPersonContactViewModel { PersonID = 4, FirstName = "Jo", MiddleName= "", LastName="Smith" },
new FindPersonContactViewModel { PersonID = 5, FirstName = "Jody", MiddleName= "", LastName="Smith" },
new FindPersonContactViewModel { PersonID = 6, FirstName = "Johnn", MiddleName= "", LastName="Smith" },
new FindPersonContactViewModel { PersonID = 7, FirstName = "Jake", MiddleName= "", LastName="Smith" },
new FindPersonContactViewModel { PersonID = 8, FirstName = "Jock", MiddleName= "", LastName="Smith" },
};
Things to notice 1. PersonID of 3197908 is in here 3 times BECAUSE they have a different ContactRoleTypeId and ContactType
So thus my GOAL is to end up joining the data to have result collection like this
PersonID | FirstName | LastName | ContactRoleTypeId | ContactType
3197908 John Smith 1 Farmer
3197908 John Smith 2 Plumber
3197908 John Smith 3 Landscaper
4444 Jody Smith
55555 Jon Smither
I was trying to join
var ids = from azed in azedIdentity
join personRole in personContactRoles on azed.PersonID equals personRole.PersonID
select personRole;
I am thinking I need to have 2 nexted foreach loops ?????
Collection Poco model used for both sources is this:
public class FindPersonContactViewModel
{
public int PersonID { get; set; }
public string AZEDID { get; set; }
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
public string EmailAddress { get; set; }
public int? GenderTypeId { get; set; }
public string DOB { get; set; }
public int ContactRoleTypeId { get; set; }
public string ContactType { get; set; }
public int PersonTypeId { get; set; }
public string PreferredPhone { get; set; }
public string PreferredEmail { get; set; }
public string PhysicalAddress { get; set; }
public bool ExistInContactManager { get; set; }
public bool ActionType { get; set; }
public bool IsInContactManager { get; set; }
}
var result = from azed in azedIdentity join personRole in personContactRoles on azed.PersonID equals personRole.PersonID
into r1 from p in r1.DefaultIfEmpty() select
new FindPersonContactViewModel{PersonID = azed.PersonID, FirstName = azed.FirstName, LastName = azed.LastName,
ContactRoleTypeId = p == null ? 0 : p.ContactRoleTypeId, ContactType = p == null ? "" : p.ContactType};
You can achieve expected results with following:
var results = personContactRoles.Join(
azedIdentity,
x => x.FirstName + x.LastName,
y => y.FirstName + y.LastName,
(x, y) => new FindPersonContactViewModel()
{
PersonID = y.PersonID,
FirstName = y.FirstName,
LastName = y.LastName,
ContactRoleTypeId = x.ContactRoleTypeId,
ContactType = x.ContactType
});
however you will get 0 at ContactRoleTypeId since your POCO FindPersonContactViewModel.ContactRoleTypeId is not nullable integer

Order GroupBy Asc based on Two more Properties using LINQ C#

My GroupBy is performing well. I'm getting the Output
I need to Sort the Group Names
The Brown Color Block, represents the Group.
The Red Color Box within the Brown Color Block, represents the Manager
Peter Block (Brown Box) Should Come first
Raj Block (Brown Box) Should Come Second
Sunny Block (Brown Box) Should Come Third
Each Block Should Group By Boss(Manager) and Assistant (Boss don't have the
SID). After GroupBy the Name should be in Sorted Order, within the Group
the Assistant Names are also in the Sorted Order.
The Model Classes:
public class Person
{
public int ID { get; set; }
public int SID { get; set; }
public string Name { get; set; }
public string Department { get; set; }
public string Gender { get; set; }
public string Role { get; set; }
}
public class Boss
{
public int ID { get; set; }
public int SID { get; set; }
public string Name { get; set; }
public string Department { get; set; }
public string Gender { get; set; }
public string Role { get; set; }
public List<Person> Employees { get; set; }
}
The Main Functionality Source Code:
void Main()
{
List<Boss> BossList = new List<Boss>()
{
new Boss()
{
ID = 101,
Name = "Sunny",
Department = "Development",
Gender = "Male",
Role = "Manager",
Employees = new List<Person>()
{
new Person() {ID = 101, SID = 102, Name = "Peter", Department = "Development", Gender = "Male", Role = "Assistant"},
new Person() {ID = 101, SID = 103, Name = "Emma Watson", Department = "Development", Gender = "Female", Role = "Assistant"},
}
},
new Boss()
{
ID = 104,
Name = "Raj",
Department = "Development",
Gender = "Male",
Role = "Manager",
Employees = new List<Person>()
{
new Person() {ID = 104, SID = 105, Name = "Kaliya", Department = "Development", Gender = "Male", Role = "Assistant"},
new Person() {ID = 104, SID = 103, Name = "Emma Watson", Department = "Development", Gender = "Female", Role = "Assistant"},
},
},
new Boss()
{
ID = 102,
Name = "Peter",
Department = "Development",
Gender = "Male",
Role = "Manager",
Employees = new List<Person>()
{
new Person() {ID = 102, SID = 105, Name = "Kaliya", Department = "Development", Gender = "Male", Role = "Assistant"},
new Person() {ID = 102, SID = 103, Name = "Emma Watson", Department = "Development", Gender = "Female", Role = "Assistant"},
}
}
};
List<Person> EmpList = BossList.SelectMany(i =>
new[] {
new Person()
{
ID = i.ID,
SID = i.SID,
Name = i.Name,
Gender = i.Gender,
Department = i.Department,
Role = i.Role
}
}.Concat(i.Employees)
).ToList().GroupBy(s => s.ID).SelectMany(h => h.GroupBy(g => g.SID).SelectMany(u => u.OrderBy(k=> k.Name))).ToList();
}
You can do by adding the ThenBy extension method after the Order by to apply the secondary sort. In fact, the ThenBy can be called multiple times for sorting on multiple property. I have modified the last line of your code to show how you can achieve this.
).ToList().GroupBy(s => s.ID).SelectMany(h => h.GroupBy(g => g.SID).SelectMany(u => u.OrderBy(k=> k.Name).ThenBy(l => l.<<secondproperty>>))).ToList();
The datastructure already establishes the groups. There is no need to re-group.
List<Person> result = (
from boss in BossList
order by boss.Name
let orderedEmployees = boss.Employees.OrderBy(emp => emp.Name)
let bossPerson = new Person(boss)
let people = new List<Person>() { bossPerson }.Concat(orderedEmployees)
from person in people
select person).ToList();
If you prefer the lambda syntax:
List<Person> result = BossList
.OrderBy(boss => boss.Name)
.SelectMany(boss => {
IEnumerable<Person> orderedEmployees = boss.Employees.OrderBy(emp => emp.Name);
Person bossPerson = new Person(boss);
return new List<Person>() { bossPerson }.Concat(orderedEmployees);
})
.ToList();

Categories