Linq join 3 list <T> - c#

I have three lists that I need to join together
class Person
{
public int PersonID{ get; set; }
public string FirstName{get; set;}
public string LastName {get; set;}
}
class Traffic
{
public DateTime Date{ get; set; }
public int PersonID;
public int TrafficID;
}
class TrafficType
{
public int TrafficID { get; set; }
public string Description { get; set; }
}
List<Person> Persons=GetPersons();
List<TrafficType> TrafficTypes=GetTrafficTypes();
List<Traffic> Traffics=GetTraffics();
I need an output like:
PersonID FirstName LastName Date Description
1001 David ... 2011/07/19 sample description

from person in Persons
from traffic in traffics
from trafficType in trafficTypes
where trafficType.TrafficID = traffic.TrafficID
where traffic.PersonID = person.PersonID
select new
{
PersonID = person.PersonID,
....
}

var result = Persons.Join(
Traffics,
person => person.PersonID,
trafic => trafic.PersonID,
(person, trafic) => new
{
PersonId = person.PersonID,
FirstName = person.FirstName,
LastName = person.LastName,
Date = trafic.Date,
TraficId = trafic.TrafficID
}).Join(
TrafficTypes,
a => a.TraficId,
traficType => traficType.TrafficID,
(a, traficType) => new
{
PersonId = a.PersonId,
FirstName = a.FirstName,
LastName = a.LastName,
Date = a.Date,
Description = traficType.Description
});

Here's a complete code sample with Linq query expression code that should get exactly what you're looking for:
using System;
using System.Collections.Generic;
using System.Linq;
class Person
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
class Traffic
{
public DateTime Date { get; set; }
public int PersonId { get; set; }
public int TrafficId { get; set; }
}
class TrafficType
{
public int Id { get; set; }
public string Description { get; set; }
}
class Program
{
static void Main(string[] args)
{
var persons = new List<Person>()
{
new Person()
{
Id = 1001,
FirstName = "David",
LastName = "Jones",
},
};
var trafficTypes = new List<TrafficType>()
{
new TrafficType()
{
Id = 456,
Description = "sample description",
},
};
var traffics = new List<Traffic>()
{
new Traffic()
{
PersonId = 1001,
TrafficId = 456,
Date = DateTime.Now,
},
};
var joinedData = from p in persons
from t in traffics
from tt in trafficTypes
where p.Id == t.PersonId
&& tt.Id == t.TrafficId
select new
{
PersonId = p.Id,
FirstName = p.FirstName,
LastName = p.LastName,
// Remove time component, if present
Date = t.Date.Date,
Description = tt.Description,
};
foreach (var item in joinedData)
{
Console.WriteLine("{0}\t{1}\t{2}\t{3}\t{4}"
, item.PersonId
, item.FirstName
, item.LastName
, item.Date.ToShortDateString() // Don't print the time
, item.Description
);
}
}
}
The program output is:
1001 David Jones 7/19/2011 sample description

You can put them all in a class e.g. (Problem), then use a method for your output.
class Problem
{
public Problem()
{
}
public void Show()
{
// implement your output here
}
}
Or, if you are using Windows Forms app and interisting in Tables, you can use DataGridView control. For more information about it, visit http://msdn.microsoft.com/en-us/library/e0ywh3cz.aspx
Or, use DataGrid: http://www.codeproject.com/KB/grid/usingdatagrid.aspx

Related

Using where in LINQ select new statement for specific columns

I'm working on a class assignment and got a bit lost in LINQ.
I have 3 tables, 'oltandok' contains the data of persons, 'preferenciak' contains the preferred vaccine of that person with 3 columns:
an FK for table oltandok
a number indicating the order of preferences (1 is highest, 6 is lowest preferred)
an FK for another table containing the data on the vaccines called 'vakcinak'
I would like to display the data in a DataGridView the following way:
Personal data and the preferred vaccines in different columns:
Pref1 - Name of the vaccine where pref == 1
Pref2 - Name of the vaccine where pref == 2
etc.
This is where I am with my code, but I'm not sure how to select the preferences properly.
manu_rogz.DataSource = ( from x in context.oltandok
join y in context.preferencia on x.TAJ equals y.oltandok_FK
select new
{
TAJ = x.TAJ,
Nev = x.nev,
Szuletesnap = x.birthdate,
Pref1 = ???
Pref2 = ???
}
).ToList();
Because the preferenciak table contains multiple rows per person, you will need to perform some grouping.
Here is some very rough code which illustrates one way to do that.
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main(string[] args)
{
var persons = new List<Person> { new Person { ID = 11, PersonName = "Alice" }, new Person { ID = 22, PersonName = "Bob" } };
var vaccines = new List<Vaccine> { new Vaccine(){ ID = 111, VaccineName= "Pfizer" }, new Vaccine(){ ID = 222, VaccineName = "Moderna" } };
var preferences = new List<VaccPref>
{
new VaccPref() { Person_FK = 11, Preference = 1, Vaccine_FK = 111 },
new VaccPref() { Person_FK = 11, Preference = 2, Vaccine_FK = 222 },
new VaccPref() { Person_FK = 22, Preference = 1, Vaccine_FK = 222 },
new VaccPref() { Person_FK = 22, Preference = 2, Vaccine_FK = 111 }
};
var prefsWithVaccNames = preferences.Join(vaccines, p => p.Vaccine_FK, v => v.ID, (pref, vaccine) => new Tuple<VaccPref, string>(pref, vaccine.VaccineName));
var groupedPrefs = prefsWithVaccNames.GroupBy(p => p.Item1.Person_FK);
var personPrefs = new List<PersonPrefs>();
foreach (var group in groupedPrefs)
{
personPrefs.Add(
new PersonPrefs()
{
Person_FK = group.Key,
Pref1 = group.Single(v => v.Item1.Preference == 1).Item2,
Pref2 = group.Single(v => v.Item1.Preference == 2).Item2,
});
}
var personPrefsWithPersonNames =
personPrefs.Join(
persons,
pp => pp.Person_FK,
p => p.ID,
(pp, p) => new NamedPersonPrefs() { Name = p.PersonName, Pref1 = pp.Pref1, Pref2 = pp.Pref2 }).ToArray();
}
}
class Person
{
public int ID { get; set; }
public string PersonName { get; set; }
}
class VaccPref
{
public int Person_FK { get; set; }
public int Preference { get; set; }
public int Vaccine_FK { get; set; }
}
class Vaccine
{
public int ID { get; set; }
public string VaccineName { get; set; }
}
class PersonPrefs
{
public int Person_FK { get; set; }
public string Pref1 { get; set; }
public string Pref2 { get; set; }
}
class NamedPersonPrefs
{
public string Name { get; set; }
public string Pref1 { get; set; }
public string Pref2 { get; set; }
}
This is a self-contained C# program which should produce a result similar to what you're after. You will of course need to adjust the class definitions (and change the table names) to suit your needs.
I've used LINQ's fluent syntax but you can use the SQL-like version if you prefer.

Is there a way to append a calculated field to Select()?

Is there a way to add a field to an IQueryable (or List<> or whatever) without having to rewrite an entire "new {list, of, all, fields}". I have a dynamic field who can have different formulas depending on the situation. Here's a basic example :
public class User
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
List<User> users = new List<User>();
users.Add(new User { FirstName = "Foo", LastName = "Bar"} );
users.Add(new User { FirstName = "Bar", LastName = "Foo"} );
I would like to add a field later ( just before the output ) without removing or rewriting all the existing fields
var betterUser = users.Select(u => new { FullName = u.FirstName + " " + u.LastName });
This will only return FullName, but I want to keep FirstName and LastName. In my real code, I have about 15 fields and about 1.2 billion rows that will be cleaned up before adding the last field so if I add all possible calculated fields directly to the model, the performance takes a serious hit.
You could do something like this
public class User
{
public string FirstName { get; set; }
public string LastName { get; set; }
public object dynamicField {get; set;}
}
List<User> users = new List<User>();
users.Add(new User { FirstName = "Foo", LastName = "Bar"} );
users.Add(new User { FirstName = "Bar", LastName = "Foo"} );
foreach (var obj in users)
{
obj.dynamicField = obj.FirstName + " " + obj.LastName;
}
This way you would not have to remove or rewrite all the existing fields.
Your user class needs a property to do this: but you can do it:
public class Animal
{
public string Name { get; set; }
public bool HasLegs { get; set; }
public bool IsFoo { get; set; }
public string FooLegs { get; set; }
}
private static string legsString(bool hasLegs)
{
return hasLegs ? "Has Legs" : "Has No Legs";
}
static void Main(string[] args)
{
List<Animal> animals = new List<Animal>()
{
new Animal()
{
Name = "Foo",
HasLegs = true
},
new Animal()
{
Name = "Kangaroo",
HasLegs = true
},
new Animal()
{
Name = "Snake",
HasLegs = false
}
};
var fooAnimals = animals.Select(s => new Animal
{
Name = s.Name,
HasLegs = s.HasLegs,
IsFoo = (s.Name == "Foo" && s.HasLegs),
FooLegs = $"{s.Name} {legsString(s.HasLegs)}"
}).AsQueryable<Animal>();
}
}

Unable to show the contents of the second table in Fiddler

I am using Linq to join 2 tables. I am able to get the contents of the first table but getting the second table as null. How can I extract the contents of the second table also into a single JSON object. My code is below:
public static IEnumerable<Tbl_Students> GetAllStudents()
{
StudentDBEntities dataContext = new StudentDBEntities();
var query = (from student in dataContext.Tbl_Students
join subject in dataContext.Tbl_Subjects on student.Roll_Number equals subject.Roll_Number
select new
{
Roll_Number = student.Roll_Number,
FirstName = student.FirstName,
LastName = student.LastName,
Class = student.Class,
Gender = student.Gender,
Science = subject.Science,
Social = subject.Social,
Mathematics = subject.Mathematics,
Total = subject.Total
}).ToList().Select(s => new Tbl_Students
{
Roll_Number = s.Roll_Number,
FirstName = s.FirstName,
LastName = s.LastName,
Class = s.Class,
Gender = s.Gender
});
return query;
}
The two table structures are:
Student
public class Student
{
public int Roll_Number { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int Class { get; set; }
public string Gender { get; set; }
}
Subject
class Subject
{
public int Roll_Number { get; set; }
public int Science { get; set; }
public int Social { get; set; }
public int Mathematics { get; set; }
public int Total { get; set; }
}
I am getting everything except the subjects in Fiddler.
Adding the image
Problem is that you are never populating Tbl_Subjects from your query. I have updated your query to populate Tbl_Subjects.
public static IEnumerable<Tbl_Students> GetAllStudents()
{
StudentDBEntities dataContext = new StudentDBEntities();
var query = (from student in dataContext.Tbl_Students
join subject in dataContext.Tbl_Subjects on student.Roll_Number equals subject.Roll_Number
select new
{
Roll_Number = student.Roll_Number,
FirstName = student.FirstName,
LastName = student.LastName,
Class = student.Class,
Gender = student.Gender,
Science = subject.Science,
Social = subject.Social,
Mathematics = subject.Mathematics,
Total = subject.Total
}).ToList().Select(s => new Tbl_Students
{
Roll_Number = s.Roll_Number,
FirstName = s.FirstName,
LastName = s.LastName,
Class = s.Class,
Gender = s.Gender,
Tbl_Subjects = new Tbl_Subjects ()
{
Science = s.Science,
Social = s.Social,
Mathematics = s.Mathematics,
Total = s.Total
Roll_Number = s.Roll_Number
};
});
return query;
}

How merge two lists of different objects?

Using C# with LINQ, how can I merge two lists of different objects, say, Seminar and Conference?
They have some common and some different fields/properties and do not share unique id.
class Seminar
{
int id,
DateTime joinDate,
string name
}
class Conference
{
Guid confNumber,
DateTime joinDate
Type type
}
I have a list of:
List<Seminar>
List<Conference>
I need to merge them into a super List:
List<Object>
A code snippet would be great help.
If you just want a single List<object> containing all objects from both lists, that's fairly simple:
List<object> objectList = seminarList.Cast<object>()
.Concat(conferenceList)
.ToList();
If that's not what you want, then you'll need to define what you mean by "merge".
Following code works fine for me, if this is your definition of Merge
One solution
List<A> someAs = new List<A>() { new A(), new A() };
List<B> someBs = new List<B>() { new B(), new B { something = new A() } };
List<Object> allS = (from x in someAs select (Object)x).ToList();
allS.AddRange((from x in someBs select (Object)x).ToList());
Where A and B are some classes as follows
class A
{
public string someAnotherThing { get; set; }
}
class B
{
public A something { get; set; }
}
Another Solution
List<A> someAs = new List<A>() { new A(), new A() };
List<B> someBs = new List<B>() { new B(), new B { something = string.Empty } };
List<Object> allS = (from x in someAs select (Object)new { someAnotherThing = x.someAnotherThing, something = string.Empty }).ToList();
allS.AddRange((from x in someBs select (Object)new { someAnotherThing = string.Empty, something = x.something}).ToList());
Where A and B are having class definition as
class A
{
public string someAnotherThing { get; set; }
}
class B
{
public string something { get; set; }
}
Simple method of pure code
internal class Person
{
public int Id { get; set; }
public string UserName { get; set; }
}
internal class User
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
internal class UserPerson
{
public int Id { get; set; }
public string UserName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
private static void Main(string[] args)
{
Person[] people = new Person[3] { new Person { Id = 1, UserName = "AliUserName" }, new Person { Id = 2, UserName = "MortezaUserName" }, new Person { Id = 3, UserName = "SalarUserName" } };
User[] users = new User[4] { new User { FirstName = "ali", LastName = "Barzegari" }, new User { FirstName = "Morteza", LastName = "Sefidi" }, new User { FirstName = "Salar", LastName = "Pirzadeh" }, new User { FirstName = "Babak", LastName = "Hasani" } };
UserPerson[] userPeople = new UserPerson[people.Length > users.Length ? people.Length : users.Length];
if (people.Length > users.Length)
for (int i = 0; i < people.Length; i++)
{
userPeople[i] = new UserPerson
{
Id = people[i].Id,
UserName = people[i].UserName,
FirstName = users.Length <= i ? "" : users[i].FirstName,
LastName = users.Length <= i ? "" : users[i].LastName
};
}
else
for (int i = 0; i < users.Length; i++)
{
userPeople[i] = new UserPerson
{
Id = people.Length <= i ? 0 : people[i].Id,
UserName = people.Length <= i ? "" : people[i].UserName,
FirstName = users[i].FirstName,
LastName = users[i].LastName
};
}
Console.ReadLine();
}

Sorting, Customizing and Generating with Raw Data or Objects

I have a list of Customers records , may be in thousands ,
I want to generate reports (Crystal or MS ) like in hierarchy.
it should be something like this
Customer By country , then Customer by City with in a Country , Then Customers in Areas , and then Male and Female in those area.
I also want to show customer plusminus Calculated from Top .
Like 4 Customer like in NYC and all have +500 , so I have value in US 2000;
any Idea , Hint algorithm how I can achieve this?
here is the Customer Object and example Customers .
public class Customer
{
public int CutIND { get; set; }
public string CustName { get; set; }
public string Country { get; set; }
public string City { get; set; }
public string Area { get; set; }
public string Gender { get; set; }
public int plusMinus { get; set; }
}
and example customers
Customer c1 = new Customer();
c1.CutIND = 123445;
c1.CustName = "Sajjad";
c1.Country = "US";
c1.City = "NYC";
c1.Area = "BLueArea";
c1.plusMinus = -560;
Customer c2 = new Customer();
c2.CutIND = 43432;
c2.CustName = "Mike";
c2.Country = "UK";
c2.City = "London";
c2.Area = "SomeArea";
c2.plusMinus = 9000;
You can use LINQ queries to relatively easily group data hierarchically at multiple levels.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
public class Customer
{
public int CutIND { get; set; }
public string CustName { get; set; }
public string Country { get; set; }
public string City { get; set; }
public string Area { get; set; }
public string Gender { get; set; }
public int plusMinus { get; set; }
public Customer(int CutIND, string CustName, string Country, string City, string Area, string Gender, int plusMinus)
{
this.CutIND = CutIND;
this.CustName = CustName;
this.Country = Country;
this.City = City;
this.Area = Area;
this.Gender = Gender;
this.plusMinus = plusMinus;
}
}
class Program
{
static void Main(string[] args)
{
Customer[] customers = new Customer[] {
new Customer(123445, "Sajjad", "US", "NYC", "BLueArea", "M", -560),
new Customer(43432, "Mike", "UK", "London", "someArea", "M", 9000),
new Customer(20001, "Mathilde", "OS", "Vienna", "WienerWald", "F", 8192),
new Customer(20002, "Harry", "US", "NYC", "Broooklyn", "M", 50),
new Customer(20003, "Jim", "OS", "Vienna", "AIS", "M", 12000),
new Customer(20004, "Bill", "US", "MSP", "Excelsior", "M", 90)
};
var CityGroups =
from c in customers
group c by new { Country = c.Country, City = c.City } into cities
select new { Country = cities.Key.Country, City = cities.Key.City, Total = cities.Sum(c => c.plusMinus), Residents = cities };
var CountryGroups =
from city in CityGroups
group city by city.Country into countries
select new { Country = countries.Key, Cities = countries, Total = countries.Sum(c => c.Total) };
foreach (var country in CountryGroups)
{
Console.WriteLine("{0} (Total = {1})", country.Country, country.Total);
foreach (var city in country.Cities)
{
Console.WriteLine(" {0} (Total = {1})", city.City, city.Total);
foreach (var r in city.Residents)
{
Console.WriteLine(" {0} {1} {2} {3}", r.Area, r.CustName, r.Gender, r.plusMinus);
}
}
}
}
}
}

Categories