comparison of two prop in dictionary - c#

I had to switch my List<> to Dictionary<int,List> now I am struggling how to compare two values.
I need to compare Ids and i am getting an underline here
(m => m.Id == lastOpenedArticle.Id);
that 'KeyValuePair<int, Article>' does not contain a definition for 'Id' and no accessible extension method 'Id' accepting a first argument of type 'KeyValuePair<int, Article>'
The article contains the definition
var openedArticle = allOptimizationData.LastOpenedArticles.FirstOrDefault(m => m.Id == lastOpenedArticle.Id);
public static async void SaveLastOpenedArticle(ArticleDetailData lastOpenedArticle)
{
await Task.Run(() =>
{
Directory.CreateDirectory(Path.Combine(LangUpStorage.OptimalizationDataFolder));
var allOptimizationData = DeserializeAllOptimizationData();
var openedArticle = allOptimizationData.LastOpenedArticles.FirstOrDefault(m => m.Id == lastOpenedArticle.Id);
}
Is there a way to achieve comparison like i had when i used List with the dictionary?
Sorry I have never used Dictionary before plus i am a beginner

Working on the assumption that LastOpenArticles is the Dictionary<int,Article> that you mentioned:
var openedArticle = allOptimizationData.LastOpenedArticles
.FirstOrDefault(m => m.Value.Id == lastOpenedArticle.Id);

Keyvalue pairs have the Value and the Key properties.
so either m.Key or m.Value.Id depending on how you are using the ids
https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.keyvaluepair-2?view=netcore-3.1

Related

linq : find values in list exist in another list or not?

I have a small problem, from the below code first we are finding hostelStops with ID, hostelName, InTime
then we are taking wardenkduty with DutyName, tasksStart
now, I want to check whether hostelStops-hostelName is exist in taskList-taskStart
var hostelStops =
(from item in Hostel.Where(tn => tn.HostelNumber == hostelNumber).SelectMany(x => x.Hostels)
select new
{
Id = item.Id,
hostelName = hostelName.Single(p => p.Id == item.hostelId).SymbolicName,
InTime = item.InTimeTime
}).ToList();
var taskList =
(from row in filteredWardenDuty
select new
{
dutyName = row.DutyName,
taskStart = row.WardenTasks.Select(x => x.Context.Start.PlaceId)
}).ToList();
so, i have written below code
var isExist =
from row in taskList
select new
{
task = row.taskStart.Contains((from item in hostelStops select item.hostelName ))
};
but, receiving error as
Error CS1929 'IEnumerable' does not contain a definition for 'Contains' and the best extension method overload 'MemoryExtensions.Contains<IEnumerable>(ReadOnlySpan<IEnumerable>, IEnumerable)' requires a receiver of type 'ReadOnlySpan<IEnumerable>'
You can't do someCollection.Contains(otherCollection), you have to do someCollection.Any(item => otherCollection.Contains(item)) (in english: "is any item from this collection contained in that collection") so something like:
row.taskStart.Any(ts => (from item in hostelStops select item.hostelName).Contains(ts));
It would perhaps be more performant to make your hostelstops into a hashset:
var hs = (from item in hostelStops select item.hostelName).ToHashSet();
...
row.taskStart.Any(ts => hs.Contains(ts));
You haven't been clear about what types these are; this will only work if the two collections are of the same basic type (like string) or a type that is able to be compared properly even if theyre different instances, for which you may have to write a custom comparer or an Equals/GetHashcode pair of overrides

How use Reflection to condition multiple properties to check for equality in a LINQ .Where statement, depending on what class is passed?

I'm trying to generalize a duplicate checker function, which depending on which type of object, checks the properties said class has (provided in a configuration) are equal to those in another list.
I have decided to create a Dictionary, which will accept a type string for the key (Book, Author, Shop, etc.) and an array of properties that need to be equal.
Example of Dictionary enties:
"Book", ["Title", "CoverImage", "NumberOfPages"]
"Author", ["Name", "Address", "SomethingElse"]
Then, I pass an object to the function and use Reflection to get the name of the type...
obj.GetType().Name;
... which I then use to fetch the right KVP from the Dictionary, meaning that if I pass a Book object, I get "Book". We then use that to get the configuration via ...
configDictionary["obj.GetType().Name"]
... which gives us the array of strings that are the properties that we need to check equality on.
I've gotten to the part where I need something along the lines of
list.Where(x => --> for each of the strings in the array - x.GetType.GetProperty(string) && --> same for next string && same for next string
... and then I need to top it off with an...
x.Id != obj.Id
To make sure we check for duplicates based on our logic (different id's and matches on all properties but has different Id's thus - a duplicate).
The end query should look like
Books:
someList.Where(x =>
x.Title == obj.Title
&& x.CoverImage == obj.CoverImage
&& x.NumberOfPages == obj.NumberOfPages
&& x.Id != obj.Id)
.FirstOrDefault();
Authors:
someList.Where(x => x.Name == obj.Name
&& x.Address == obj.Address
&& x.SomethingElse == obj.SomethingElse
&& x.Id != obj.Id)FirstOrDefault();
Try to avoid reflection because it can slow down your application. As an alternative you can create a dictionary and put all comparators into it:
var configDictionary = new Dictionary<string, List<Func<object, object, bool>>>
{
{
"Book",
new List<Func<object, object, bool>>
{
(b1, b2) => ((Book)b1).Title == ((Book)b2).Title,
(b1, b2) => ((Book)b1).CoverImage == ((Book)b2).CoverImage,
(b1, b2) => ((Book)b1).NumberOfPages == ((Book)b2).NumberOfPages,
(b1, b2) => ((Book)b1).Id != ((Book)b2).Id,
}
},
// same for Authors
};
Now you can use it in Where method:
var typeName = obj.GetType().Name; // here we using Reflection but once per collection, not per each item
var first = someList.Where(x => configDictionary[typeName].All(f => f(x, obj))).FirstOrDefault();
Also, because FirstOrDefault also has overload that accept predicate last line can be rewritten to:
var first = someList.FirstOrDefault(x => configDictionary[typeName].All(f => f(x, obj)));
A better solution will be creating custom attribute which will tag property. Then in class override default method Equals which will get all properties with this attribute and return equality.

How to Link two IDs from different classes in MVC5 to display certain information

I am trying to link up the RestaurantId in the RestaurantReservationEventsTbl with the RestaurantID in the RestaurantTbl to display reservations that are only made for the currently logged in restaurant.
I am receiving the following error in my code operator == cannot be applied to operands of type int and iqueryable int
Here is what I am doing in my home controller
var RestaurantIDRestaurantTbl = from r in db.Restaurants select r.RestaurantID;
//var listOfRestaurantsReservations = db.RestaurantReservationEvents.ToList();
var listOfRestaurantsReservations = db.RestaurantReservationEvents.Where(x => x.RestaurantID == RestaurantIDRestaurantTbl).ToList();
//return View(restaurants.Where(x => x.RestaurantEmailAddress == UserEmail).ToList());
//create partial view called _RestaurantReservation
return PartialView("_RestaurantReservations", listOfRestaurantsReservations);
You have to change your code to materialize the restaurantIds like this:
var RestaurantIDRestaurantTbl = (from r in db.Restaurants
select r.RestaurantID).ToList();
Then you may change the code as below for the comparison to work:
var listOfRestaurantsReservations = db.RestaurantReservationEvents.Where(x => RestaurantIDRestaurantTbl.Contains(x.RestaurantID)).ToList();
Anyway this is not the best solution. I will write another example for you, just try this example if it is working or not and let me know for the result.
I would considering changing the code as below to be much more efficient:
var listOfRestaurantsReservations = (from r in db.Restaurants
join e in db.RestaurantReservationEvents
on r.RestaurantID equals e.RestaurantID
//where r.RestaurantID == something //if where condition needed
select e).ToList();
If your tables are not connected with foreignkeys please consider to read this documentation here to make a better structure of the tables since they are related to each-other.
If your tables are related as in documentation article you might have something like that:
var RestaurantIDRestaurantTbl = db.Restaurants.SingleOrDefault(x => x.RestaurantID == something);
if(RestaurantIDRestaurantTbl != null)
{
var listOfRestaurantsReservations = RestaurantIDRestaurantTbl.RestaurantReservationEvents.ToList();
}
{
// This will give you a list of IDs
var RestaurantIDRestaurantTbl = db.Restaurants
.Select(p => p.RestaurantID)
.ToList();
// Using .Any() is a better choice instead of .Contains()
// .Contains is used to check if a list contains an item while .Any will look for an item in a list with a specific ID
var listOfRestaurantsReservations = db.RestaurantReservationEvents
.Where(p => RestaurantIDRestaurantTbl.Any(r => r.pRestaurantID == p))
.ToList();
}

Traverse over a nested Dictionary using the nested dictionary as key in c# via Linq

so I have the following situation:
I have a Dictionary like this:
Dictionary<Dictionary<string, string>, DateTime> expireDates = new Dictionary<Dictionary<string, string>, DateTime>();
where the inner dictionary'first string is filled by a value (lets call it articlenumber) and the second string is filled by a value named articleCode. Together, they are the Key to get the preferred DateTime I need.
I try to get the DateTime via Linq like this in a WCF-Service:
var cmps= dbComponents.Select(component => new WCFObjects.Contracts.Component()
{
ArticleNumber = component.ArticleNumber,
Sortiment = component.SortimentsCode,
... //irrelevant values
//PSEUDOCODE, NOT WORKING
ExpireDate = expireDates.Where(x => x.Value.Where(y => x.Key.Where(
z => z.Key == component.ArticleNumber
&& z.Value == component.SortimentsCode))).FirstOrDefault()
}).ToList();
But I am struggling to create the preferred linq-query to get the value where key == articlenumber and value == sortimentscode. I tried a few approaches, most of them giving me the error
Error 'System.DateTime' does not contain a definition for 'Where' and no extension method 'Where' accepting a first argument of type 'System.DateTime' could be found (are you missing a using directive or an assembly reference?)
but I can't figure it out and hope someone could help me out here.
Best regards,
getoveritde
Try
ExpireDate = expireDates.Where(
x => x.Key.Any(
y => y.Key == component.ArticleNumber && y.Value == component.SortimentsCode)
).Select(z => z.Value).FirstOrDefault()

Linq lambda entities , does not contain definition

I have this query code here :
//Get all records based on ActivityID and TaskID.
public IList<Model.questionhint> GetRecords1(int listTask, int listActivity)
{
IList<Model.questionhint> lstRecords = context.questionhints.ToList();
return lstRecords.GroupBy(x => new { x.QuestionNo, x.ActivityID, x.TaskID }).Where(a => a.TaskID == listTask && a.ActivityID == listActivity).ToList();
}
The error lies in the .Where statement, it says does not contain definition for ActivityID and TaskID.
Full error :
'System.Linq.IGrouping' does not contain a definition for 'ActivityID' and no extension method 'ActivityID' accepting a first argument of type 'System.Linq.IGrouping' could be found (are you missing a using directive or an assembly reference?)
I am weak in query statements, basically I want to retrieve records from database where activity id = something and task id = something and group them by questionNo, activityId and Task ID .
The simplest fix here is: filter (where) before you group; this will also reduce the work that the grouping has to do:
return context.questionhints
.Where(a => a.TaskID == listTask && a.ActivityID == listActivity)
.GroupBy(x => new { x.QuestionNo, x.ActivityID, x.TaskID })
.ToList();
The reason it isn't working in your original code is, as already mentioned, that GroupBy returns a sequence of groups - each of which has a .Key (your anonymous type) and is itself an IEnumerable<T> sequence of the items in that group.
However! Your method claims to return IList<Model.questionhint>; your grouped data is not, and will never be, an IList<Model.questionhint> - it will be an IList<IGrouping<{some anonymous type, Model.questionhint>>. So: you cannot group like that if you are claiming that it is an IList<Model.questionhint> - and since the grouping is an anonymous type, you can't change the return type to match. You have two choices:
don't group
group by something declarable (a custom type, or a Tuple<...>), and change the return type to match
For example:
public IList<IGrouping<Tuple<int,int,int>,Model.questionhint>>
GetRecords1(int listTask, int listActivity)
{
return context.questionhints
.Where(a => a.TaskID == listTask && a.ActivityID == listActivity)
.GroupBy(x => Tuple.Create(x.QuestionNo, x.ActivityID, x.TaskID))
.ToList();
}
change it to Where(a => a.Key.TaskID == listTask && a.Key.ActivityID == listActivity)
You are dealing with an IGrouping, of which the Key property is the anonymous object new { x.QuestionNo, x.ActivityID, x.TaskID }
This solves the original error, but now we are trying to return IGroupings, which is not the correct return type. A cleaner way of doing it would be
var groups = lstRecords.GroupBy(x => new { x.QuestionNo, x.ActivityID, x.TaskID }).Where(a => a.Key.TaskID == listTask && a.Key.ActivityID == listActivity);
IList<Model.questionhint> questionHints = new List<Model.questionhint>();
foreach(var group in groups)
{
questionHints.AddRange(group);
}
return questionHints;
Note, this code is untested. You can do all this in one linq line (as im sure Mark will), however i tend to split it for readability
Alternative
If your goal is to get all the questionHints that match the criteria, what is wrong with a simple Where clause?
lstRecords.Where(a=>a.TaskID == listTask && a.ActivityID == listActivity).ToList();
References
http://msdn.microsoft.com/en-us/library/bb344977.aspx

Categories