LINQ match objects in set A in set B - c#

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.

Related

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
}

How to: Select Distinct objects<T> from multiple Lists<T> with 1 or more properties of same type

So this is a design question that has vexed me for hours, and I have to reach out to the group for an assist. I have a collection with thousands of shared entities, and need to retrieve a distinct list of managers from three different properties contained in two lists, two managers in the Stores list, and one manager from warehouse collection.
To simplify the problem, I've written a simple console program that highlights the challenge. I tossed it together, so yes, I know it's inefficient, but it demonstrates the problem:
public class Program
{
static void Main(string[] args)
{
DistributionGroup d = new DistributionGroup();
Console.WriteLine("====Store Managers====");
foreach(Manager m in d.Stores.Select(m => m.StoreManager).Distinct())
{
Console.WriteLine("{0}:{1}", m.Id, m.Name);
}
Console.WriteLine("=====Inv. Managers=====");
foreach (Manager m in d.Stores.Select(m => m.InventoryManager).Distinct())
{
Console.WriteLine("{0}:{1}", m.Id, m.Name);
}
Console.WriteLine("===Warehouse Managers===");
foreach (Manager m in d.Warehouses.Select(m => m.WarehouseManager).Distinct())
{
Console.WriteLine("{0}:{1}", m.Id, m.Name);
}
}
}
public class DistributionGroup
{
private Manager m1 = new Manager(1, "Bob Wilson");
private Manager m2 = new Manager(2, "Chris Warren");
private Manager m3 = new Manager(3, "Mike Olsen");
private Manager m4 = new Manager(4, "Aaron Erikson");
public List<RetailStore> Stores;
public List<Warehouse> Warehouses;
public DistributionGroup()
{
RetailStore s1 = new RetailStore(1, m1, m1);
RetailStore s2 = new RetailStore(2, m1, m3);
RetailStore s3 = new RetailStore(3, m2, m2);
Warehouse w1 = new Warehouse(1, m4);
Warehouse w2 = new Warehouse(2, m2);
Stores = new List<RetailStore>();
Stores.Add(s1);
Stores.Add(s2);
Stores.Add(s3);
Warehouses = new List<Warehouse>();
Warehouses.Add(w1);
Warehouses.Add(w2);
}
}
public class Manager
{
public int Id;
public string Name;
public Manager(int id, string name)
{
Id = id; Name = name;
}
}
public class RetailStore
{
public int Id;
public Manager StoreManager;
public Manager InventoryManager;
public RetailStore(int id, Manager mgr, Manager inventoryMgr)
{
Id = id;
StoreManager = mgr;
InventoryManager = inventoryMgr;
}
}
public class Warehouse
{
public int Id;
public Manager WarehouseManager;
public Warehouse(int id, Manager mgr)
{
Id = id;
WarehouseManager = mgr;
}
}
What I need to do is generate a distinct list of Managers from all three properties:
RetailStore.StoreManager
RetailStore.InventoryManager
Warehouse.WarehouseManager
So following the example, the console output would simply be:
1:Bob Wilson
2:Chris Warren
3:Mike Olsen
4:Aaron Erikson
I've been trying to figure out the syntax, but LINQ is not a strength, and I'm hoping the group can help me out.
Seems like you need to use Enumerable.Union
var allManagers = d.Stores.Select(m => m.StoreManager).Union(d.Stores.Select(m => m.InventoryManager))
.Union(d.Warehouses.Select(m => m.WarehouseManager));
foreach (Manager m in allManagers)
{
Console.WriteLine("{0}:{1}", m.Id, m.Name);
}
Note that Union returns unique values from input sequences so no need to call Distinct.

Find the item with the lowest value of a property within a list

Let's say I have this class:
class Person {
public int ID;
public string Name;
}
And then I have a list of Person's.
List<Person> persons = new List<Person>();
Which is filled with alot of random persons. How do I query the list to get the person with the lowest ID? The objects in the list is in a random order so the person with the lowest ID might not be the very first element. Can I achieve this without sorting the list first?
this is without sorting the list and just iterates the list once.
Person minIdPerson = persons[0];
foreach (var person in persons)
{
if (person.ID < minIdPerson.ID)
minIdPerson = person;
}
You can use MinBy method from More Linq library:
var person = persons.MinBy(x => x.ID);
If you can't use a third party library you can get the min ID first and then get the person that has the min ID:
var minID = person.Min(x => x.ID);
var person = persons.First(x => x.ID == minID);
Use the Min extension method of LINQ:
persons.Min(p => p.ID)
EDIT:
My bad, the previous method returns only the lowest ID, so in case you'd like to use only built-in LINQ methods, here you go:
persons.Aggregate(
(personWithMinID, currentPerson) =>
currentPerson.ID <= personWithMinID.ID ? currentPerson : personWithMinID)
List<AnswerInfo> answerinfo;
Public void SampleFunction()
{
answerinfo = new List<AnswerInfo>();
//custom hash table storing elapsed time for all users
float LocalScoreTime = (float.Parse)((string)local.CustomProperties["elapsedTime"]);
AnswerInfo objc = new AnswerInfo();
if (CorrectAnswer)
{
foreach (PhotonPlayer _player in PhotonNetwork.otherPlayers)
{
objc.ID = _player.ID;
objc.AnsCorrect = (bool)_player.CustomProperties["RemoteAnswer"];
objc.AnsTime = (float.Parse)((string)_player.CustomProperties["elapsedTime"]);
answerinfo.Add(objc);
}
//This can work too and can be used in future
//var minID = answerinfo.Min(x => x.AnsTime);
//var person = answerinfo.First(x => x.AnsTime == minID);
AnswerInfo minTimePerson = answerinfo[0];
float minTime = 30;
foreach (AnswerInfo user in answerinfo)
{
if (user.AnsCorrect)
{
if (user.AnsTime < minTime)
{
minTime = user.AnsTime;
minTimePerson = user;
}
}
}
Debug.LogFormat("Remote User ID with correct Answer: {0} and lowest time {1}",minTimePerson.ID,minTimePerson.AnsTime);
if(LocalScoreTime < minTimePerson.AnsTime)
{
local.AddScore(1);
localplayerscore_textfield.color = Color.green;
}
}
}
[Serializable]
public class AnswerInfo
{
public bool AnsCorrect;
public float AnsTime;
public int ID;
}

Linq Count Expression

Suppose the following Employee class (yes I know I shouldn't publicly expose Lists but this is just an example):
class Employee
{
public string Name {get; set;}
public List<string> Skills {get; set;}
}
Skills is just a list of skills the employee has, for example "programming", "customer service", etc.
Now suppose I have a List<Employee> CurrentEmployees, and I also have another employee, Employee NewHire that is not in the CurrentEmployees list.
My goal is to use a lambda expression to count how many employees in CurrentEmployees have at least one skill that NewHire also has. So for example, if CurrentEmployees contains one employee with Skills{'Programming', 'Troubleshooting'}, and another employee with Skills{'Accounting','Finance'}, and NewHire has Skills{'Programming','Networking'}, I would want an expression that returns 1, because the first employee in the list also has 'Programming' as a skill... is this possible?
Thanks for any help.
currentEmployees.Count(ce =>
ce.Skills.Intersect(newHire.Skills).Any())
var currentEmployees = new List<Employee>
{
new Employee { Skills = new List<string> { "Programming", "Troubleshooting" } },
new Employee { Skills = new List<string> { "Accounting", "Finance" } },
};
var newHire = new Employee { Skills = new List<string> { "Programming", "Networking" } };
var count = currentEmployees.Count(e => e.Skills.Any(newHire.Skills.Contains));
// count == 1
If performance was important, I would use a HashSet<string>:
var newHireSkills = new HashSet<string>(newHire.Skills);
var count = currentEmployees.Count(e => e.Skills.Any(s => newHireSkills.Contains(s)));

Using Linq not equals

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

Categories

Resources