C# linq with two Lists - c#

I'm stuck at the following Problem:
I want to load one List, but I can pass more than one Parameter what could be the criteria to find the Lists I want.
Now I have the following structure:
House{
Name;
ID;
Alias;
}
Also I Have:
Person{
Name;
Alias;
}
This means, 1 house can hold multiple persons and multiple persons with the same name can be in multiple houses.
So now I want to call my Function e.G. "GetHouses(string criteria)" as criteria could be:
a Name of a house
an ID of a house
a part of the name of the house
a Name of one of the Persons in the house
Now I just read every house and it's data and I select afterwards by the criteria.
IMPORTANT NOTE: I can not change the logic until here!
So now as I try to find the matching criteria I came up with using LINQ as it is very fast. It works as long as I don't compare to the Persons:
result = (from x in result
where (
(string.Equals(x.Name, criteria))
|| (string.Equals(x.ID, criteria))
|| (x.Name.Contains(criteria))
select x).ToList();
Now I want to load every Person to the houses I found and check if a Name of the Persons in the house would match the criteria.
Is there a way to do this within the LINQ I have already?
Or do I have to go though the result with:
result.ForEach(x => ...)
Would be nice if it would work with the LINQ.
I did a similar Logic with the
result.FindAll(new Predicate<House>((x) => { ... LoadPersons(criteria) {... } }));
But that took to long.
Kind regards,
Asat0r

Presuming that you have a PersonList in your House-class you could use Enumerable.Any:
var matchingHouses = from house in allHouses
where string.Equals(house.Name, criteria)
|| string.Equals(house.ID, criteria)
|| house.Name.Contains(criteria)
|| house.PersonList.Any(resident => string.Equals(resident.Name, criteria))
select house;
If you have a method to return the "residents" you can use this. If you later want to access these persons you could create an anonymous type to store them:
var housesAndResidents = from house in allHouses
let matchingResidentList = LoadPersons(house.ID)
.Where(resident => string.Equals(resident.Name, criteria))
.ToList()
where string.Equals(house.Name, criteria)
|| string.Equals(house.ID, criteria)
|| house.Name.Contains(criteria)
|| matchingResidentList.Any()
select new { house, matchingResidentList };
You can access these properties in the following way:
var matchingHouseList = housesAndResidents.ToList();
// you don't need the list, you can use foreach directly,
// but whenever you access housesAndResidents you will execute that query
// ToList materializes this query into a collection, so you can enumerate it or use the Count property
foreach(var x in matchingHouseList )
{
Console.WriteLine("House:{0} Matching-Resident(s):{1}"
, x.house.Name
, String.Join(", ", x.matchingResidentList.Select(r => r.Name)));
}

Here is a simple solution using three classes: Program, House and Person.
The class House "houses" (pun intended) a list of the class Person (and your own values: name, id and alias). In this way the residents are a part of the house and not stored somewhere else. Storing the residents outside the House class is the same as having your lunchbox's contents outside the box in your backpack.
The class Person stores the basic information about the house's residents. Having a list of residents inside the House class makes it easier to compare the search criteria with the people.
In the class Program you'll find the Main and the FindHouses classes. These kinda explains themselves
class Person
{
public string name;
public string alias;
public Person(string _name, string _alias = "")
{
name = _name;
alias = _alias;
}
}
class House
{
public string name;
public string id;
public string alias;
public List<Person> people = new List<Person>();
public House(string _name, string _id, string _alias = "")
{
name = _name;
id = _id;
alias = _alias;
}
}
class Program
{
static List<House> houses = new List<House>();
static void Main(string[] args)
{
// Add houses here
foreach (House house in FindHouses("criteria"))
{
// Do stuff
}
}
// Find all the houses in which the criteria exists
static List<House> FindHouses(string criteria)
{
// Return the list of all houses in which the criteria exists
return houses.Where(h =>
h.name.Contains(criteria) ||
h.id == criteria ||
h.alias.Contains(criteria) ||
h.people.Any(p =>
p.name.Contains(criteria) ||
p.alias.Contains(criteria))).ToList();
}
}
I dont know if this is too big of a change in your code, but anyways, I hope this helps.
I know you mentioned you didn't want to load the persons into the houses, but this makes the search for houses based on "all values" much easier

Related

How to return multiple nodes using shortest path?

I'm trying to recreate this in C#:
MATCH p=shortestPath((bacon:Person {name:"Kevin Bacon"})-[*]-(meg:Person {name:"Meg Ryan"}))
RETURN p
But all examples i found that uses shortest path only returns a list of same type of nodes. In this example i need to return both People and Movies nodes.
So it starts something like this :
var shortestPath = graphClient.Cypher
.OptionalMatch("p = shortestPath((bacon: Person )-[*] - (meg: Person ) )")
.Where((Person bacon) => bacon.name == "Kevin Bacon ")
.AndWhere((Person meg) => meg.name == "Meg Ryan")
.Return(?)
.Results;
What should i write instead of question mark?
Graph looks like this:
Update :
The only information I found on neo4j client's shortest path is this Return All Nodes in Shortest Path as Object List but I can't figure it out.
Good question - and tough to work out, I think I've got there - though - it's probably not the most performant.
As you say - the problem is multiple types - so let's get things setup, I'm using an extension method to make this look a bit nicer code wise:
public static class Extensions
{
public static T ToObject<T>(this string value)
{
//deserialize into a 'Node' first
var node = JsonConvert.DeserializeObject<Node>(value);
//then take the 'data' from that and 'ToObject' to the correct thing.
return ((JObject) node.data).ToObject<T>();
}
private class Node{
public object data {get;set;}
}
}
The ToObject<T> method is what we'll use to convert our output to objects.
I've got the standard set of POCO for representing the data (albeit with bad naming conventions for the properties):
public class Movie {
public string title {get;set;}
public string tagline {get;set;}
}
public class Person {
public string name {get;set;}
public int born {get;set;}
}
public class RolesRel {
public IEnumerable<string> roles {get;set;}
}
OK - now we're ready to go. I've changed the Return to look like this:
var shortestPath = graphClient.Cypher
.OptionalMatch("p = shortestPath((bacon: Person)-[*]-(meg: Person ) )")
.Where((Person bacon) => bacon.name == "Kevin Bacon")
.AndWhere((Person meg) => meg.name == "Meg Ryan")
.Return(p => new {
Nodes = Return.As<IEnumerable<string>>("nodes(p)"),
Relationships = Return.As<IEnumerable<RolesRel>>("rels(p)")
});
string is the only universal option we've got for deserializing multiple node types. In this scenario, as the relationships are all the same, we're all good on that front to just use a POCO - but if they weren't you'd end up doing what we're doing for Nodes for them as well.
Next, I run through the results:
foreach (var result in shortestPath.Results)
{
foreach (var node in result.Nodes)
{
if(node.Contains("name"))
Console.WriteLine(node.ToObject<Person>().name);
else if(node.Contains("title"))
Console.WriteLine(node.ToObject<Movie>().title);
else {
Console.WriteLine("Unknown Node Type: " + node);
}
}
}
I'm doing a naive check here for a property name I know is in one node type, but not another - and you may want to do it another way - but this works.
Obviously - I'm just outputting to the screen - but you will have access to the object itself.

How to pass a generic selector Func<T, U> to some method in order to perform search?

I have a class with some fields.
public abstract class RegisteredUser : Transformable //implements interface
{
protected int id;
protected bool active;
protected string name;
protected string lastName;
protected string email;
protected string username;
protected string password;
protected int institution;
//OTHER STUFF FROM THIS CLASS GO HERE..`
}
Assistant : RegisteredUser
{
}
Once I load all data from .txt file, I'll have a static dictionary like this:
public static Dictionary<int, Transformable> assistants = null; //this will be filed with objects and their id's
Now, my assignment is to achieve functionality of search for this console app. Since, all of sub classes will have fields from abstract class, user of this app should have a choice to search for any object Assistant, Professor or Administrator by any of its fields.
I would like to achieve this by using LINQ/lambdas. My idea is to create some generic method which will accept a selector (or some lambda expression ), based on whatever field is being searched for.
I am completely new to LINQ and C# and generally to programming, so I hope that I'm not talking nonsense here.
public static List<RegisteredUser> search(Dictionary<int, Transformable> map,
Func<bool,RegisteredUser> selector,
Transformable userType)
{
if (userType is Assistant)
{
List<RegisteredUser> list = map.Select(item => (Assistant) item.Value)
.ToList()
.Select(selector)
.ToList();
}
//other if/else statements
return list;
}
Of course, lines in Visual Studio are red :) Basically, I want to be be able send something like
user => user.Name.Contains("some string which will be entered by the end user") to generic function.
You don't really need to write a separate function to do what you want to achieve, it can all be done with Linq directly:
var users = new Dictionary<int, Transformable>
{
{0, new Assistant { Name = "Joe" }},
{1, new Professor { Name = "John" }},
{2, new Assistant { Name = "Jane" }}
};
var assistants = users.Values.OfType<Assistant>().Where(a => a.Name == "Jane");
assistants is an IEnumerable<Assistant> with Name equal to "Jane" and will contain only 1 entry from the above dictionary. You could add a ToList() to the end of that to create list alternatively
You can then add whatever predicate you want:
var assistants = users.Values.OfType<Assistant>().Where(a => a.Name == "Jane" && a.LastName == "Smith" && a.Active);
You could wrap the above in a function but what you'll end up with won't be much shorter than the above.
As an extension method it would be like this:
public static class Extensions
{
public static IEnumerable<T> Find<T>(this IEnumerable<Transformable> list, Func<T,bool> predicate) where T :Transformable
{
return list.OfType<T>().Where(predicate.Invoke);
}
}
making the call slightly shorter:
var assistants = users.Values.Find<Assistant>(a => a.Name == "Jane");
This is an example of the fluent API pattern so you can use it in a Linq chain like any other Linq function.

Succinct LINQ filter expression

I have an MVC controller that will filter a product list based on a category.
Products = repository.Products.Where(p => category == null || p.Category1 == "category1" );
If I wanted to let the user filter the product with two categories, I would have to add in another if statement that contains Category1 and Category2. I can imagine if I have more categories, and the user can choose category 1,3,5 and so on, the permutation will get crazily large.
Is there a proper way of doing this?
I am assuming that your object model is defined along the lines of:
public class Product
{
// ...
public Category Category1 { get; set; }
public Category Category2 { get; set; }
public Category Category3 { get; set; }
// ...
}
(where you might be using strings instead of having a category class)
If the object model is within your control, then I would recommend changing it so that a product has a collection of categories rather than several named properties for Category1, Category2, Category3 etc, so more like this:
public class Product
{
// ...
public IList<Category> Categories { get; set; }
// ...
}
If the product class is fixed and it already has multiple individual category properties, I would recommend writing an extension method for your product class that returns a list of categories that are non-null. That way you can write a where expression more succinctly.
For example:
public static class ProductExtension
{
public static IList<Category> GetCategories(this Product product)
{
List<Category> categories = new List<Category>();
if (product.Category1 != null)
{
categories.Add(product.Category1);
}
if (product.Category2 != null)
{
categories.Add(product.Category2);
}
// etc.
return categories;
}
}
...which could then be used along the lines of
repository.Products.Where(p => p.GetCategories().Contains("category1"));
Another option is to create a ProductFilter object to do the filtering for you.
Give the ProductFilter class a field for every category that is possible to filter on, which each store predicates, and a PassesFilter(Product p) method which determines whether p passes the predicate for all categories where a predicate has been set, e.g.
method PassesFilter(Product p):
if Category1Filter is not null:
if p does not pass Category1Filter:
return false
if Category2Filter is not null:
if p does not pass Category2Filter:
return false
return true
(Excuse the pseudo-code, I don't do C# and it's late)
So you could use it like so:
ProductFilter pf = new ProductFilter();
...
/*build up your filters for all categories that apply in this search...*/
pf.ColourFilter = (Product p) => { return p.Colour == "Red"; };
pf.PriceFilter = (Product p) => { return p.Price > 100.00; };
...
Products = repository.Products.Where(p => category == null || pf.PassesFilter(p) );
You could also easily implement the PassesFilter method differently to handle OR instead of AND (or create a class for each implementation).
I know that using predicates in the way I described would allow you to put a price predicate in the colour predicate field, but I just thought I'd throw this example out there to illustrate the concept of using an object to do the work of lambdas :-)
1.You may use Expression to constructor condition expression
2.Use expression in the linq.

Hydrate property via joining another List<Property> on unique Id

I have two lists, one which is a list of Equipment and one which is a list of WorkflowItems with an Equipment property. The Equipment property within the List<WorkflowItem> list only has one of it's values hydrated, ProcessId. The List<Equipment> has two properties hydrated, ProcessId and Name. I want to hydrate the List<WorkflowItem>.Equipment.Name with the value from the single Equipment record in the List<Equipment>
This LINQ query below will select a generic item out doing basically what I'm looking for, but I would rather just fill in the original list.
var list = from item in workflowItems
join equipment in barcodeEquipmentList on
item.Equipment.ProcessId equals equipment.ProcessId
select new
{
ProcessId = item.Equipment.ProcessId,
EquipmentName = equipment.Name
};
Edit
The list is going to be relatively small, even doing something like this would be fine (aside from the fact that this does not work)
workflowItems.ForEach(x => x.Equipment = from e in barcodeEquipmentList
where e.Process.Id == x.Equipment.Process.Id
select e
);
...final edit
but this does work:
workflowItems.ForEach(x => x.Equipment = barcodeEquipmentList
.Where(e => e.Process.Id == x.Equipment.Process.Id)
.FirstOrDefault());
This piece of code should match your needs:
public class Equipment {
public int ProcessId { get; set; }
public string Name { get; set; }
}
public class WorkflowItem {
public Equipment { get; set; }
public void LoadEquipmentFrom(IEnumerable<Equipment> cache){
var equipment = cache.FirstOrDefault(e => e.ProcessId == Equipment.ProcessId);
if(equipment != null)
Equipment.Name = equipment.Name;
}
}
You could also assign the instance from cache to the existing one, it wouldn't matter since both must have the same Identifier Equipment = equipment;. That would be easier if you have more properties to set. To optimize further use an IDictionary<int, Equipment> instead of that IEnumerable<Equipment>, because you'll be reading that collection very often.
I'm guessing you are implementing a kind of ORM, in this case I can give you a good advice: "There is already something out there that'll fit your needs.".
Since the dataset was not very big (less than 20 records), I was able to this as below without a hit to performance
workflowItems.ForEach(x => x.Equipment = barcodeEquipmentList
.Where(e => e.Process.Id == x.Equipment.Process.Id)
.FirstOrDefault());

C# Finding element in List of String Arrays

I cannot solve a problem for several hours now.
Here is a simplified scenario.
Let's say there is a list of people with their bids. I'm trying to find a person with the highest bid and return the name. I am able to find the highest bid, but how to I output the name?
List<String[]> list = new List<String[]>();
String[] Bob = { "Alice", "19.15" };
String[] Alice = {"Bob", "28.20"};
String[] Michael = { "Michael", "25.12" };
list.Add(Bob);
list.Add(Alice);
list.Add(Michael);
String result = list.Max(s => Double.Parse(s.ElementAt(1))).ToString();
System.Console.WriteLine(result);
As a result I get 28.20, which is correct, but I need to display "Bob" instead. There were so many combinations with list.Select(), but no success. Anyone please?
The best solution from an architectural point of view is to create a separate class (e.g. Person) that contains two properties Name and Bid of each person and a class Persons that contains the list of persons.
Then you can easily use a LINQ command.
Also instead of storing bids as string, think if bids as floating point or decimal values would be better (or store it in cents and use an int).
I don't have a compiler by hand so it's a bit out of my head:
public class Person
{
public string Name { get; set; }
public float Bid { get; set; }
public Person(string name, float bid)
{
Debug.AssertTrue(bid > 0.0);
Name = name;
Bid = bid;
}
}
public class Persons : List<Person>
{
public void Fill()
{
Add(new Person("Bob", 19.15));
Add(new Person("Alice" , 28.20));
Add(new Person("Michael", 25.12));
}
}
In your class:
var persons = new Persons();
persons.Fill();
var nameOfHighestBidder = persons.MaxBy(item => item.Bid).Name;
Console.WriteLine(nameOfHighestBidder);
This works in the simple example. Not sure about the real one
var result = list.OrderByDescending(s => Double.Parse(s.ElementAt(1))).First();
You can use Jon Skeet's MaxBy.
For usage you can see this question
e.g. in this case
list.MaxBy(s => Double.Parse(s.ElementAt(1)))[0]
More here
Should work:
var max = list.Max(t => double.Parse(t[1]));
list.First(s => double.Parse(s[1]) == max)[0]; // If list is not empty
After finding result just do as below:
list.First(x=>x[1] == result)[0]

Categories