Linq lambda using where - c#

I am still learning lambda and Linq from Microsoft site and trying to write some simple example myself to get a deeper understanding of the cool stuff. The more I learn the more I find that stuff interesting but the learning curve is steep. Once again, I will need some more assistance.
Basically, I have class called item where it has properties NodeID, Weight and Category.
I have also a class called Recipient which represent recipient receiving items.
I have also a 2 dimensional boolean table that shows the interaction of one item against the other. If an item1 with ID NodeID1 is not supposed to have with item2 with ID Node2 then the table[Node1][Node2] should have a value true.
What I am trying to find out is the list of recipients that receive stuff that should not be receiving together, in other word stuff that has value true in the table.
public class Recipient
{
private Dictionary<int,item> _itemsReceivedList=new Dictionary<int,item>(); //itemID
private int _recipientID;
public int RecipientID{ get; set; }
public List<int> getListItemInCategory(int category)
{
return _itemsReceivedList.Where(x => x.Value.Category == category).Select(x => x.Value.NodeID).ToList();
}
}
public class item
{
public int NodeID { get; set; }
public int Weight { get; set; }
public int Category { get; set; }
}
In my main program:
private bool[][] prohibitedMatrix; //prohibitedMatrix[NodeID1][NodeID2]=true means it is prohibited to have Item NodeID1 and NodeID2 together
private Dictionary<int,Recipient> recipients = new Dictionary<int,Recipient>();
private Dictionary<int, item> items = new Dictionary<int,item>();
given an item with NodeID1, find recipients that has x in _itemReceivedList so that prohibitedMatrix[x.NodeID][NodeID1]= true
recipients.Where(x=>x.Value.getListItemInCategory(items[NodeID].Category)
&& "The NodeID in listItemInCategory and NodeID1 is not
true)
.Select(x=>x.Value.RecipientID)
Thank you for your help!

To have a one-liner this should work:
var result = recipients
.Values
.Select(r => new
{
RecipientID = r.RecipientID,
Items = r.getListItemInCategory(items[NodeID].Category)
})
.Where(ri => ri.Items.Any(i => prohibitedMatrix[i.NodeID][NodeID]))
.Select(ri => ri.RecipientID);
or this:
var result = recipients
.Values
.Where(r => r
.getListItemInCategory(items[NodeID].Category)
.Any(i => prohibitedMatrix[i.NodeID][NodeID]))
.Select(r => r.RecipientID);
However better to introduce some utility functions here and partition this. Or use plain-old foreach.

I think I found answer to my own question. I may or may not be correct. Please let me know if I am wrong.
This is what I think:
recipients.Where((x,y)=>x.Value.getListItemInCategory(items[NodeID1].Category).Contains(y) && prohibitedMatrix[y][NodeID1]).Select(x=>x.Value.RecipientID).ToList()

Related

Replace Duplicates with unique string in Dictionary<string,list<object>>

I have List<ParametersDetails>. Parameters and ParametersDetails class is like -
public class ParameterDetails
{
public string Name { get; set; }
public List<Parameter> Parameters{get;set;}
}
public class Parameter
{
public string Name { get; set; }
public string Type { get; set; }
public string Value { get; set; }
public string AccountName { get; set; }
}
I want ParameterDetails list should not contain any parameter with duplicate name. If any duplicate parameter name found I want to replace the name with Parametername+ parameterDetails name from the dictionary.
I can do it by traversing items and then modify items but I want to do it with lesser code.
The problem is how to traverse and find duplicates from list ..?
Is it possible in Linq?
What I am doing right now - I have taken all the parameters in 1 list and find out duplicates
var hasDupes = dflist.GroupBy(x => new { x.Name })
.Where(x => x.Skip(1).Any()).ToArray();
Next, I am selecting the item from List
parameterdetails.Select(x => x.Parameters.Where(p => p.Name == dupeList.Key.ToString())).ToList();
Now I don't want to loop through ParameterDetials List to modify the items.
Is any easier way?
Ex-
I am having 2 items in ParameterDetails like -
ParameterDetails:
[
{
name: "test1",
Parameters:[
{
"Name":"param1",
"Type":"paramtype1",
"Value":"value1",
"AccountName":"accname1"
},
{
"Name":"param2",
"Type":"paramtype2",
"Value":"value2",
"AccountName":"accname2"
}]
},
{
name: "test2",
Parameters:[
{
"Name":"param1",
"Type":"paramtype11",
"Value":"value11",
"AccountName":"accname11"
},
{
"Name":"param2",
"Type":"paramtype22",
"Value":"value22",
"AccountName":"accname22"
}]
}]
If I am having param1 as a duplicate name so in that I want to replace it as "param1+test2" so that it will be unique.
Yo find they duplicated items in a list you can use LINQ, this is the code using it:
var duplicates = dflist.GroupBy(s => s) .SelectMany(grp => grp.Skip(1));
That code will return all duplicated items in dflist. If you want to select all object that have an unique attribute value, you can use this code line:
var withoutNameDuplicates = dflist.GroupBy(s => s.Name).Select(grp => group.First());
Then you can change the objects Name attribute to the duplicated objects by, using this code:
var nameChangedDuplicates = duplicates.Select( s => {s.Name = s.Name + "something"; }).ToList();
If you only want to get all the items from the list without duplicates, you can apply Dinstinct() method to dflist, that method will return an IEnumerable<T> result:
var withoutDuplicates = dflist.Distinct();
And, if you want to return a List<T> instead IEnumerable<T>, you must use ToList() method like this:
var withoutDuplicates = dflist.Distinct().ToList();
You can get more information about it in these pages:
c# - How to get duplicate items from a list using LINQ?
Enumerable.DistinctMethod (IEnumerable)
C# - Update all objects in a collection using LINQ

Filter Object List with string array list C# lambda expression

I had tried a similar SO answer here which not worked and might be missing something in my case.
Background:
I'm trying to pull a list of Trade Instruments from an external API ( around 8k records ) from which I need around 10 only. So trying to filter it as below , but the filter results are 0 .
Model:
public class Trade
{
public int ID { get; set; }
public string Scrip { get; set; }
public int Quantity { get; set; }
}
Filtering:
List<Trade> trades;
using (StreamReader sr = new StreamReader(Server.MapPath("~/Utils/trades.json")))
{
trades = JsonConvert.DeserializeObject<List<Trade>>(sr.ReadToEnd());
}
List<Instrument> instruments = GetInstruments(Exchange: "NY");// count 8k
var result = instruments.Where(x => trades.Any(n => x.Name.Contains(n.Scrip))); //count 0
Also, tried to fetch the Scrip names from the trades list as string array and used for filtering which also didn't work.
Please advise and thanks in advance.
Thanks guys for the help , was a silly mistake
changed Contains to Equals and its working as expected.
instruments.Where(x => trades.Any(n => x.Name.Equals(n.Scrip)));

How to write a lambda to get one property based on another property in an object

I'm trying to do what I thought would be a very simple think using Linq lambda, it probably is, but I can't find an example in any tutorial.
I have a simple class with a few properties. I want to get a list of one of the properties based on the value on another value in that class.
Below is an example of the code, using Linq to get the correct results:
public class Client
{
public int ClientId { get; set; }
public int ClientWorth { get; set; }
public strin ClientName { get; set; }
}
.
.
.
.
List<Client> allClients = this.GetAllClients();
List<string> richClients = (
from c in allClients
where c.ClientWorth > 500
select c.ClientId.ToString()).ToList();
Can someone tell me how to do this using a lambda
I can do the following:
List<Clients> richClients = allClients.Where(x => x.ClientWorth >500)
Which give me a list of all clients, but I would like to get a string list back with just the client ids.
After filtering by client worth value you should project results - i.e. select only client id value:
allClients.Where(c => c.ClientWorth > 500).Select(c => c.ClientId.ToString()).ToList()
Further reading: Enumerable.Select

LINQ SUM data based on Array Results

I'm stuck on this issue. I know it can be done nicely with LINQ (I don't want to use multiple foreach loops), but I simply cannot get it to work. Here is the problem:
I have two classes:
Class Invoice
public class Invoice
{
public int InvoiceID { get; set; }
public string Name { get; set; }
public DateTime DueDate { get; set; }
public Invoice(int ID, string sName, DateTime dt_Date)
{
this.InvoiceID = ID;
this.Name = sName;
this.DueDate = dt_Date;
}
}
and Class Activity
public class Activity
{
public int ActivityID { get; set; }
public int InvoiceID { get; set; }
public int Count { get; set; }
public double Price { get; set; }
public Activity(int ID, int InvoiceID, int iCount, double dPrice)
{
this.ActivityID = ID;
this.InvoiceID = InvoiceID;
this.Count = iCount;
this.Price = dPrice;
}
}
The logic is that Each Invoice contains multiple Activities. E.g. you have a bill from a shop (Invoice), which includes items such as bread, butter, milk (Activities).
What I want to do is that based on user selected Date, I want to return total amount paid (basically I want to perform SUM of all bills from specific Date period).
Here is what I have:
//user selected DateTime - for the sake of test we make it current day
DateTime selectedDate = DateTime.Now;
//Retrieve invocies that match user selected Date
var retrievedInvoices = invList
.Where(n => n.DueDate.ToShortDateString() == selectedDate.ToShortDateString());
This is fine, as we have retrieved list of all Invoices based on desired Date. But now? I tried something as following:
//now make SUM of activities that match retrievedInvoices -> look at their
//ID's and if match is found, then multiply price x count
double dResult = invActivity
.Where(n => retrievedInvoices.Where(x=>x.InvoiceID == n.InvoiceID))
.Sum(n => n.Price * n.Count);
But it is not working. I am not that proficient in LINQ so there might be more elegant way of doing it (maybe in one line of code) but I don't know.
Could you guys help me with this one, please?
EDIT:
Interesting thing to note also for others that might be looking at this thread: I have first tried to use List in order to retrieve my list of invoices that match specific time period (DateTime_From and DateTime_To); but it behaved strangely as sometimes it worker correctly, sometimes not (even though the code was correct). After I changed List <Invoice> retrievedInvoice to var retrievedInvoice it suddenly worked without a problem. I don't understand why this is so, but I will definitely be more aware next time of what type do I use.
Thanks again folks!
Your code looks fine, but try some changes like this:
use .Date to compare just Dates without Time (hours, minutes, etc..) and select just eh InvoiceID proeprty into a array
var retrievedInvoices = invList.Where(n => n.DueDate.Date == selectedDate.Date)
.Select(x => x.InvoiceID);
use .Contains() to check the condition which return a bool value.
double dResult = invActivity
.Where(n => retrievedInvoices.Contains(n.InvoiceID))
.Sum(n => n.Price * n.Count);
Some changes was suggested by Tuespetre user in comments bellow!

Change the type of one of the objects property and sort it using LINQ

I want to sort the List where the objects properties are of string type.
One of the property is a time of string type, and when i try to sort it sorts like below.
1:12, 13:24, 19:56, 2:15, 26:34, 8:42.
Here the sorting is happening on string basis.
Now i want to convert that sting to double (1.12, 13.24, 19.56, 2.15, 26.34, 8.42) and sort it. Then populate the data by replacing the '.' with ':'.
I tried some thing like below, but still the sorting is happening on string basis.
public class Model
{
public string Duration { get; set; }
public string Dose { get; set; }
}
List<Model> lsModelData = new List<Model>();
//Added some model objects here
// query for sorting the lsModelData by time.
var sortedList = lsModelData.OrderBy(a => Convert.ToDouble(a.Duration.Replace(":", ".")));
I am trying to replace the time ":" with "." and then convert that to double to perform the sort operation.
Can any one please correct this statement to work this sorting properly.
If you want to sort data according to duration try this. its tested surely works for you.
public class Models
{
public string Duration { get; set; }
public string Dose { get; set; }
}
List<Models> lstModels = new List<Models>();
lstModels.Add(new Models { Duration = "101:12" });
lstModels.Add(new Models { Duration = "13:24" });
lstModels.Add(new Models { Duration = "19:56" });
List<Models> sortedList = (from models in lstModels
select new Models
{
Dose = models.Dose,
Duration = models.Duration.Replace(':','.')})
.ToList()
.OrderBy(x=>Convert.ToDouble(x.Duration))
.ToList();
I'm not sure what you really want, but if you want to return only the duration, then select it after sort
var sortedList = lsModelData.OrderBy(a => Convert.ToDouble(a.Duration.Replace(":", "."))).Select(a=> a.Duration).ToList();
or
var sortedList = lsModelData..Select(a=> a.Duration).OrderBy(a => Convert.ToDouble(a.Replace(":", "."))).ToList();
In cases like this it works best to order by length and then by content:
var sortedList = lsModelData.OrderBy(a => a.Duration.Length)
.ThenBy(a => a.Duration)
Converting database data before sorting (or filtering) always makes queries inefficient because indexes can't be used anymore.

Categories