Using Linq not equals - c#

I've 2 list collections in my C# app..A and B.
Both the collections have customer object which has Id and Name attributes.Typically, A has more items than B.
Using Linq,I want to return only those customers whose Id is in A but not in B.
How do I do this?

There are multiple approaches to take. The cleanest approach is to use the Except extension method if you have overriden Equals and GetHashCode. If you have not, there are other options.
// have you overriden Equals/GetHashCode?
IEnumerable<Customer> resultsA = listA.Except(listB);
// no override of Equals/GetHashCode? Can you provide an IEqualityComparer<Customer>?
IEnumerable<Customer> resultsB = listA.Except(listB, new CustomerComparer()); // Comparer shown below
// no override of Equals/GetHashCode + no IEqualityComparer<Customer> implementation?
IEnumerable<Customer> resultsC = listA.Where(a => !listB.Any(b => b.Id == a.Id));
// are the lists particularly large? perhaps try a hashset approach
HashSet<int> customerIds = new HashSet<int>(listB.Select(b => b.Id).Distinct());
IEnumerable<Customer> resultsD = listA.Where(a => !customerIds.Contains(a.Id));
...
class CustomerComparer : IEqualityComparer<Customer>
{
public bool Equals(Customer x, Customer y)
{
return x.Id.Equals(y.Id);
}
public int GetHashCode(Customer obj)
{
return obj.Id.GetHashCode();
}
}

If you override Equals for your customer object, then just use
A.Except(B);

Expanding on Except, providing your own equality so you don't need to change your Equals behavior.
I got this from here:
http://www.codeproject.com/KB/dotnet/LINQ.aspx#distinct
List<Customer> customersA = new List<Customer> { new Customer { Id = 1, Name = "A" }, new Customer { Id = 2, Name = "B" } };
List<Customer> customersB = new List<Customer> { new Customer { Id = 1, Name = "A" }, new Customer { Id = 3, Name = "C" } };
var c = (from custA in customersA
select custA.Id).Distinct()
.Except((from custB in customersB
select custB.Id).Distinct());

Related

How to combine to generic lists with add range and select criteria?

How to combine to generic lists with add range and select criteria? Here is a fictitious example. I can addRange but not with filtering on a criteria like room type. The "Select(mr => mr.Type == RoomType.BedRoom)" does not work. What can I use instead to just append the list where type = BedRoom?
public enum RoomType
{
Bathroom = 1,
BedRoom = 2,
Kitchen = 3,
RecRoom = 4
}
public class RoomsModel
{
public RoomType Type { get; set; }
public int Size { get; set; }
}
public List<RoomsModel> GetRooms(params)
{
var result = new List<RoomsModel>();
result = _service.GetRooms(house1);
var moreRooms _service.GetRooms(house2);
result.AddRange((from mr in moreRooms
select new RoomsModel
{
Type = mr.Type,
Size = mr.Size
}
).Select(mr => mr.Type == RoomType.BedRoom).ToList());
return result;
}
Use Where instead of Select:
result.AddRange(
from mr in moreRooms
where mr.Type == RoomType.BedRoom
select new RoomsModel
{
Type = mr.Type,
Size = mr.Size
});
Where filters items. Select projects item, i.e. transforms each item in the sequence into something else.
You need to use Where instead of Select:
result.AddRange((from mr in moreRooms
select new RoomsModel
{
Type = mr.Type,
Size = mr.Size
}
).Where(mr => mr.Type == RoomType.BedRoom).ToList());
one more way:
result.AddRange(moreRooms.Where(mr => mr.Type == RoomType.BedRoom)
.Select(mr => new RoomsModel {
Type = mr.Type,
Size = mr.Size
}));

Convert a List of Anonymous type to a list of a specific Class type

I have an abstract class "Employee".
I have a factory method that extends on this Employee class to query the database to return a list of all active employees:-
public static class EmployeeExtentions
{
public static List<Employee> FireEmployees(this List<Employee> AllCitiesEmps)
{
List<Employee> employees = new List<Employee>();
using (var ctx = new hr_employeeEntities())
{
var emp = (from x in ctx.F_Emp
join y in ctx.HR_EMPLon (x.employee_id).ToString() equals y.EMPLID
where x.employment_status == "A"
select new { x, y }).ToList();
// emp.ForEach( x => { employees.Add(new Employee(employees)); });
//var emps = emp.Select(x => PropertyCopy<Employee>.CopyFrom(x)).ToList();
//emp.ForEach(x => { employees.Add(new Employee(x)); });
}
return employees;
}
}
The var 'emp' is a list of all active employees but an anonymous list. I want to convert this into a strongly typed List of type Employee. I have 3 commented statements in my code which were my attempts.
What is the relationship between F_Emp and HR_EmpLon? It seems that these are loosely coupled through Employee_Id / EMPID depending on the table, but which table represents "Employee"?
Firstly: This does not look like it needs to be an extension method. Extension methods are meant for creating a method that will apply to a given instance of a variable. In this case "AllCitiesEmps" you are not using this instance, so at a minimum this could just be a Static method on Employee itself. (Frankly though, better served as a Repository method)
If Employee is mapped to F_Emp then the join is unnecessary:
public static List<Employee> FireEmployees()
{
using (var context = new hr_employeeEntities())
{
var employees = context.F_Emp
.Where(x => x.employment_status == "A")
.ToList();
return employees;
}
}
If Employee maps to the HR_EmpLon table and these tables do not share a common FK between them: (Disclaimer, this is a stab from memory, so it may need some tweaking. I rarely ever need to use explicit joins.)
public static List<Employee> FireEmployees()
{
using (var context = new hr_employeeEntities())
{
var employees = context.HR_EMPLon
.Join(context.F_Emp,
h => h.EMPLID,
e => e.employee_id.ToString(),
(h, e) => new {HREmployee = h, FEmployee = e})
.Where(x => x.FEmployee.employment_status == "A")
.Select(x => x.HREmployee)
.ToList();
return employees;
}
}
If an Employee is not an entity mapped to either table, but represents a mix of data from these two tables, then I would recommend setting up a View in your database to join this data, and map your entity to the view.
You can try something like this:
public static class EmployeeExtentions
{
public static List<Employee> FireEmployees(this List<Employee> AllCitiesEmps)
{
List<Employee> employees = new List<Employee>();
using (var ctx = new hr_employeeEntities())
{
var emp = (from x in ctx.F_Emp
join y in ctx.HR_EMPL on (x.employee_id).ToString() equals y.EMPLID
where x.employment_status == "A"
select new
{
x.employee_id,
x.employment_status,
//x.OtherProperties
y.EMPLID,
//y.OtherProperties
}).ToList();
employees = emp.Select(x => (new EmployeeDerived { EmployeeId = x.employee_id, EmploymentStatus = x.employment_status }) as Employee).ToList();
}
return employees;
}
private class EmployeeDerived : Employee
{
}
}
Please note, you will need to create a new derived type though, as you can't directly cast to an abstract type.

Conditional UNION in LINQ (or is there a better way than UNIONs)?

I have the following object model
public class CityData
{
public string CityName { get; set; }
public int IncidentsReported { get; set; }
}
I have a web service method (that I cannot alter the code for) that gives me data for all the cities my company's offices are in.
var data = GetCitiesData();
The above method returns only cities with incidents > 0. BUT I want all cities regardless of incident counts. I tried to solve my problem using the LINQ Union Method.
var data = GetCitiesData()
.Union(
new[]
{
// Add all cities with 0 incidents
new CityData() { CityName = "Kansas City", IncidentsReported = 0},
new CityData() { CityName = "St Louis", IncidentsReported = 0},
new CityData() { CityName = "New York", IncidentsReported = 0},
},
new CityComparer()
);
Here is my implementation of CityComparer
public class CityComparer : IEqualityComparer<CityData>
{
public bool Equals(CityData x, CityData y)
{
return x.CityName == y.CityName;
}
public int GetHashCode(CityData obj)
{
return obj.GetHashCode();
}
}
but still I am getting what can be the result of LINQ Concat not LINQ Union
Is there a better way to solve this?
You need to match GetHashCode to Equals on your comparer:
public int GetHashCode(CityData x)
{
return x.CityName.GetHashCode();
}
Define GetHashCode method like this:
public int GetHashCode(CityData x)
{
return x.CityName.GetHashCode();
}
Before Union calls Equals method it checks hash codes of objects, and if they are different, Equals method won't call.
From MSDN:
Implementations are required to ensure that if the Equals method
returns true for two objects x and y, then the value returned by the
GetHashCode method for x must equal the value returned for y.

LINQ match objects in set A in set B

Let's say we have 2 sets
A = [ PersonA,PersonB];
B = [ ManagerZ,ManagerT,ManagerY];
Result : ManagerT,ManagerY
There exists One to One mapping between the objects in A and the objects in B.
I'm interested in those objects in B for which exists such an entry in A.
For completeness let's say we are matching on property named Name
Try following
SetB.Where(b => SetA.Any(a => a.Name == b.Name))
You have to perform a join on both lists:
var query =
from person in persons
join manager in managers on person.Name equals manager.Name
select new { Person = person, Manager = manager };
This will select all data from your Person-dataset together with the corresponding data from Manager-dataset.
Alternativly you can flatten the results into a datatype providing the whole data for every match:
select new { Name = person.Name, Age = person.Age, Departement = Manager.Departement }
Or if you´re only interested on the items from B which match use simply select manager.
Try with this code:
List<BType> result = B.Where(x >= A.Exists(y => y.Name == x.Name)).ToList();
In this way you mantain only managers that exists in people list.
Also you can use Intersect.
Example:
public class Person
{
public string Name { get; set; }
}
public class PersonEqualityComparer : IEqualityComparer<Person>
{
public bool Equals(Person x, Person y)
{
return x.Name.Equals(x.Name);
}
public int GetHashCode(Person obj)
{
return obj.Name.GetHashCode();
}
}
And now you can use:
var persons = new List<Person>() { new Person { Name = "John" } };
var managers = new List<Person>() { new Person { Name = "John" } };
var results = persons.Intersect(managers, new PersonEqualityComparer());
If you want compare two different class just edit Comparer.

How to get value from IEnumerable collection using its Key?

I have IEnumerable collection like following
IEnumerable<Customer> items = new Customer[]
{
new Customer { Name = "test1", Id = 999 },
new Customer { Name = "test2", Id = 989 }
};
I want to get value using key Id
I tried like following
public int GetValue(IEnumerable<T> items,string propertyName)
{
for (int i = 0; i < items.Count(); i++)
{
(typeof(T).GetType().GetProperty(propertyName).GetValue(typeof(T), null));
// I will pass propertyName as Id and want all Id propperty values
// from items collection one by one.
}
}
If you want to retrieve a Customer name from a collection by its Id:
public string GetCustomerName(IEnumerable<Customer> customers, int id)
{
return customers.First(c => c.Id == id).Name;
}
Using LINQ you can get all customers names (values) having specific value in this way:
var valuesList = items.Where(x => x.Something == myVar).Select(v => v.Name).ToList();
For single customer name you can do this:
var singleName = items.FirstOrDefault(x => x.Id == 1)?.Name;
Obviously, the Id can be 1, 2 or any other.
Edit:
I recommend you List<Customer> instead of Customer[]
So,
var items = new List<Customer>
{
new Customer { Name = "test1", Id = 999 },
new Customer { Name = "test2", Id = 989 }
};
// I will pass propertyName as Id and want all Id propperty values
// from items collection one by one.
If I understand you correctly
public static IEnumerable<object> GetValues<T>(IEnumerable<T> items, string propertyName)
{
Type type = typeof(T);
var prop = type.GetProperty(propertyName);
foreach (var item in items)
yield return prop.GetValue(item, null);
}
Just use LINQ to achieve what you want to do. if you want to retrieve a specific value you can use where like this:
public Customer GetCustomerById(IEnumerable<Customer> items,int key)
{
return items.Where(x=>x.id==key)
.Select(x =>x.Name)
.First();
}
this will retrieve the customer who match a specific Id.
Do you want to look things up repeatedly after creating the list? If so, you might want to consider creating a dictionary to do the lookups, like so:
IEnumerable<Customer> items = new Customer[]
{
new Customer {Name = "test1", Id = 999},
new Customer {Name = "test2", Id = 989}
};
var lookup = items.ToDictionary(itemKeySelector => itemKeySelector.Id);
var result = lookup[989];
Console.WriteLine(result.Name); // Prints "test2".
I'm assuming that you don't create the collection in the first place - if you had control over creating the original collection you could use a dictionary in the first place.
private TextBox [] Collectionstextboxonpanel(Panel panel)
{
var textBoxspanel1 = panel.Controls.OfType<TextBox>(); // select controls on panle1 by type
IEnumerable<TextBox> textBoxes = textBoxspanel1; // create collection if need
TextBox[] textBoxes1 = textBoxes.ToArray(); // Array collection
return textBoxes1; // get back TextBox Collection
}

Categories

Resources