conditional Updating a list using LINQ - c#

I had a list
List<Myclass> li = new List<Myclass>();
where Myclass is
class Myclass
{
public string name {get;set;}
public decimal age {get;set;}
}
items in li looks like
i want to update `li` according to name but with `LINQ` like
li.where(w=> w.name = "di") = li.Where(w => w.name =="di").select(s => {s.age = 10;return s;}).Tolist();
li.where(w=> w.name = "marks") = li.Where(w => w.name =="marks").select(s => {s.age = 20;return s;}).Tolist();
li.where(w=> w.name = "grade") = li.Where(w => w.name =="grade").select(s => {s.age = 10;return s;}).Tolist();
and want result which looks like this
my code gives error can you please tell how i do this

cleaner way to do this is using foreach
foreach(var item in li.Where(w => w.name =="di"))
{
item.age=10;
}

You need:
li.Where(w=> w.name == "di").ToList().ForEach(i => i.age = 10);
Program code:
namespace Test
{
class Program
{
class Myclass
{
public string name { get; set; }
public decimal age { get; set; }
}
static void Main(string[] args)
{
var list = new List<Myclass> { new Myclass{name = "di", age = 0}, new Myclass{name = "marks", age = 0}, new Myclass{name = "grade", age = 0}};
list.Where(w=> w.name == "di").ToList().ForEach(i => i.age = 10);
list.ForEach(i => Console.WriteLine(i.name + ":" + i.age));
}
}
}
Output:
di:10
marks:0
grade:0

li.Where(w => w.name == "di" )
.Select(s => { s.age = 10; return s; })
.ToList();

Try this:
li.ForEach(x => x.age = (x.name == "di") ?
10 : (x.name == "marks") ?
20 : (x.name == "grade") ?
30 : 0 );
All values are updated in one line of code and you browse the List only ONE time. You have also a way to set a default value.

If you really want to use linq, you can do something like this
li= (from tl in li
select new Myclass
{
name = tl.name,
age = (tl.name == "di" ? 10 : (tl.name == "marks" ? 20 : 30))
}).ToList();
or
li = li.Select(ex => new MyClass { name = ex.name, age = (ex.name == "di" ? 10 : (ex.name == "marks" ? 20 : 30)) }).ToList();
This assumes that there are only 3 types of name. I would externalize that part into a function to make it more manageable.

Try Parallel for longer lists:
Parallel.ForEach(li.Where(f => f.name == "di"), l => l.age = 10);

How about
(from k in myList
where k.id > 35
select k).ToList().ForEach(k => k.Name = "Banana");

Related

How to get two fields with the same Id

I need to send two fields with the same Id in Altair(GraphQl).
mutation{
createGoodsOrder(goodsorder: {
deliveryDate: "2019-10-10"
goodsOrderItems: [
{ orderItemId: 54 quantity: 1 costPerUnit: 1 goodType: INGREDIENT }
{ orderItemId: 54 quantity: 2 costPerUnit: 2 goodType: INGREDIENT }
# { orderItemId: 58 quantity: 2 costPerUnit: 2 goodType: INGREDIENT }
]
}){
id
}
}
When I execute mutation, model contains both fields with the same Id but when I make Fetch, it returns only the first one. If It is not the same, Fetch returns both fields. How can I get both fields with the same Id?
var orderIngredients = _repository.Fetch<Ingredient>(e => model.GoodsOrderItems.Any(g => g.OrderItemId == e.Id)).ToList();
var orderIngredients = _repository.Fetch<Ingredient>(
e => e.IngredientType.PlaceId == model.PlaceId
&& model.GoodsOrderItems.Any(g => g.OrderItemId == e.Id && g.GoodType == GoodsTypes.Ingredient))
.Select(e => new GoodsOrderIngredientCreateModel
{
IngredientId = e.Id,
Quantity = model.GoodsOrderItems.First(i => i.OrderItemId == e.Id).Quantity,
CostPerUnit = model.GoodsOrderItems.First(i => i.OrderItemId == e.Id).CostPerUnit,
TotalPrice = model.GoodsOrderItems.First(i => i.OrderItemId == e.Id).Quantity *
model.GoodsOrderItems.First(i => i.OrderItemId == e.Id).CostPerUnit,
GoodType = GoodsTypes.Ingredient
}).Select(v => new GoodsOrderIngredient
{
Id = v.Id,
IngredientId = v.IngredientId,
Quantity = v.Quantity,
CostPerUnit = v.CostPerUnit,
TotalPrice = v.TotalPrice
}).ToList();
Mutation:
mutation.Field<GoodsOrderType>(
name: "createGoodsOrder",
arguments: new QueryArguments(
new QueryArgument<NonNullGraphType<GoodsOrderCreateInput>> { Name = nameof(GoodsOrder).ToLower() }
),
resolve: context =>
{
if (context.UserContext is GraphQLUserScopedContext userContext)
{
var goodsOrderService = userContext.ServiceScope.ServiceProvider.GetRequiredService<IVendorService>();
var model = context.GetArgument<GoodsOrderCreateModel>(nameof(GoodsOrder).ToLower());
model.PlaceId = userContext.User.PlaceId;
model.NetworkId = userContext.User.NetworkId;
var goodsOrder = goodsOrderService.CreateGoodsOrder(model);
return goodsOrder;
}
else
throw new ExecutionError(Constants.ErrorCodes.WrongUserContext);
}).RequireAuthorization(PermissionsRequirement
.CreateForPermissionSetAll(
new Dictionary<NetworkPermissions, PermissionLevels>
{ {NetworkPermissions.ERP_Cumulative, PermissionLevels.EditCreate} }));
I don't know c# but probably you don't need intermediate types
var orderIngredients = _repository.Fetch<Ingredient>(
e => e.IngredientType.PlaceId == model.PlaceId
&& model.GoodsOrderItems.Any(g => g.OrderItemId == e.Id && g.GoodType == GoodsTypes.Ingredient))
.Select(v => new GoodsOrderIngredient
{
Id = v.Id,
IngredientId = v.IngredientId,
Quantity = v.Quantity,
CostPerUnit = v.CostPerUnit,
TotalPrice = v.Quantity * v.CostPerUnit
}).ToList();
PS. If GoodsOrderIngredientCreateModel (for create mutation?) contains TotalPrice then total calculations are already in DB ?

Returning a LINQ database query from a Method

Hello everyone I have this query I am performing in multiple places. Instead of retyping the query over and over, I would like to be able to call a method that returns the query. I am not sure what to put as the return type for the method or if this is even possible to do. I use the query to write a csv file of the information, and I use the query to add items to my observable collection that is bound to a list view.
using (ProjectTrackingDBEntities context = new ProjectTrackingDBEntities())
{
var result = context.TimeEntries.Where(Entry => Entry.Date >= FilterProjectAfterDate
&& Entry.Date <= FilterProjectBeforerDate
&& (FilterProjectName != null ? Entry.ProjectName.Contains(FilterProjectName) : true))
.GroupBy(m => new { m.ProjectName, m.Phase })
.Join(context.Projects, m => new { m.Key.ProjectName, m.Key.Phase }, w => new { w.ProjectName, w.Phase }, (m, w) => new { te = m, proj = w })
.Select(m => new
{
Name = m.te.Key.ProjectName,
Phase = m.te.Key.Phase,
TimeWorked = m.te.Sum(w => w.TimeWorked),
ProposedCompletionDate = m.proj.ProposedCompletionDate,
ActualCompletionDate = m.proj.ActualCompletionDate,
Active = m.proj.Active,
StartDate = m.proj.StartDate,
Description = m.proj.Description,
EstimatedHours = m.proj.EstimatedHours
});
}
I am able to do both right now by retyping the query and performing the subsequent foreach() loops on the data. I would rather be able to do something like:
var ReturnedQuery = GetProjectsQuery();
foreach(var item in ReturnedQuery)
{
//do stuff
}
Any help would be appreciated.
You want to return IQueryable<T> with a known model that represents what it is you are returning. You should not return an anonymous type. Also you want to pass in the DbContext so it can be disposed of by the caller and not in the method otherwise you will receive an exception that the DbContext has been disposed of.
For example:
public IQueryable<ProjectModel> GetProjectQuery(ProjectTrackingDBEntities context) {
return context.TimeEntries.Where(Entry => Entry.Date >= FilterProjectAfterDate
&& Entry.Date <= FilterProjectBeforerDate
&& (FilterProjectName != null ? Entry.ProjectName.Contains(FilterProjectName) : true))
.GroupBy(m => new { m.ProjectName, m.Phase })
.Join(context.Projects, m => new { m.Key.ProjectName, m.Key.Phase }, w => new { w.ProjectName, w.Phase }, (m, w) => new { te = m, proj = w })
.Select(m => new ProjectModel
{
Name = m.te.Key.ProjectName,
Phase = m.te.Key.Phase,
TimeWorked = m.te.Sum(w => w.TimeWorked),
ProposedCompletionDate = m.proj.ProposedCompletionDate,
ActualCompletionDate = m.proj.ActualCompletionDate,
Active = m.proj.Active,
StartDate = m.proj.StartDate,
Description = m.proj.Description,
EstimatedHours = m.proj.EstimatedHours
});
}
ProjectModel.cs
public class ProjectModel {
public string Name {get;set;}
public string Phase {get;set;}
// rest of properties
}
Calling code
using (ProjectTrackingDBEntities context = new ProjectTrackingDBEntities())
{
var ReturnedQuery = GetProjectsQuery(context);
foreach(var item in ReturnedQuery)
{
//do stuff
}
}
It is easy to return the enumerator, but you can't return an enumerator for an anonymous type, unfortunately. Probably the easiest path forward for you would be to return enumerator over the full row object, like this:
public IEnumerable<TimeEntries> GetTimeEntries()
{
using (ProjectTrackingDBEntities context = new ProjectTrackingDBEntities())
{
return context.TimeEntries
.Where
(
Entry =>
Entry.Date >= FilterProjectAfterDate &&
Entry.Date <= FilterProjectBeforerDate &&
(FilterProjectName != null ? Entry.ProjectName.Contains(FilterProjectName) : true)
)
.GroupBy(m => new { m.ProjectName, m.Phase })
.Join
(
context.Projects,
m => new { m.Key.ProjectName, m.Key.Phase },
w => new { w.ProjectName, w.Phase },
(m, w) => new { te = m, proj = w }
);
}
)
}
And use it like this:
var query = GetTimeEntries();
foreach (var row in query.Select( m => new { Name = row.te.Key.ProjectName })
{
Console.WriteLine(row.Name);
}

C# Predicate builder with using AND with OR

I have the following class:
public class testClass
{
public string name { get; set; }
public int id { get; set; }
public int age { get; set; }
}
and the following code:
var list = new List<testClass>();
list.Add(new testClass { name = "name", id = 1, age = 30 });
list.Add(new testClass { name = "name", id = 2, age = 22 });
list.Add(new testClass { name = "name", id = 3, age = 20 });
list.Add(new testClass { name = "name", id = 4, age = 30 });
list.Add(new testClass { name = "name", id = 5, age = 27 });
list.Add(new testClass { name = "name", id = 6, age = 30 });
var qble = list.AsQueryable();
var pred = PredicateBuilder.New<testClass>();
pred.Or(x => x.name == "name" && x.id == 1);
pred.Or(x => x.age == 30);
var predQuery = qble.AsExpandable().Where(pred);
My aim is to create a query that returns all records where:
id = 1 and name = "name"
OR
age = 30
So for the query above, it should return the items at index 0, 1, 5
For the above query it does as I want.
However, I now want to the build the predicate by combining a set of queries, rather than explicitly defining them. So I now have the following 2 queries:
var query1 = list.Where(x => x.name == "name" && x.id == 1);
var query2 = list.Where(x => x.age == 30);
and I want to build the query based on the variables query1 and query2, without explicitly defining the conditions - as these conditions will be dynamically defined and I do not know what they are,and they will be defined in different places.
My guess is I need to do something like this (continuing from above):
var qble = list.AsQueryable();
var query1 = list.Where(x => x.name == "name" && x.id == 1);
var query2 = list.Where(x => x.age == 30);
var pred = PredicateBuilder.New<testClass>();
pred.Or(query1);
pred.Or(query2);
var predQuery = qble.AsExpandable().Where(pred);
but this is not quite correct as the predicate builder will not accept the query as a parameter.
Can this be done?
You could create two Predicate<T> and invoke them in your .Where call at the end.
var qble = list.AsQueryable();
var query1 = new Predicate<testClass>(x => x.name == "name" && x.id == 1);
var query2 = new Predicate<testClass>(x => x.age == 30);
var predQuery = qble.AsExpandable().Where(x => query1(x) || query2(x));
Or you could build another Predicate<T> beforehand and use this
var query = new Predicate<testClass>(x => query1(x) || query2(x));
var predQuery = qble.AsExpandable().Where(query);

Assigning a local variable within a lambda expression

I have a lambda expression in which need to assign a local variable to avoid calling my RetrieveAge(Datetime birthDate) two times per resultset.
My lambda expression looks as following:
result = myList.AsEnumerable().Where(f => DateHelper.RetrieveAge(f.Birthdate) >= 20 && DateHelper.RetrieveAge(f.Birthdate) <= 40).Select(x => new Person { Name = x.Name, Id = x.Id, Alias = x.Alias }).ToList();
I am trying to achieve something like the following:
var result = myList.AsEnumerable().Where(f => { var age = DateHelper.RetrieveAge(f.Birthdate); age >= 20 && age <= 40 }).Select(x => new Person { Name = x.Name, Id = x.Id, Alias = x.Alias }).ToList();
But I can't figure out how to do that properly. Any hints or suggestions would be mostly appreciated.
You are almost there - you need to add a return and a semicolon:
var result = myList
.AsEnumerable()
.Where(f => {
var age = DateHelper.RetrieveAge(f.Birthdate);
return age >= 20 && age <= 40; // <<== Here
}).Select(x => new Person {
Name = x.Name, Id = x.Id, Alias = x.Alias }
).ToList();
var result = (from f in myList.AsEnumerable()
let age = DateHelper.RetrieveAge(f.Birthdate)
where age >= 20 && age <= 40
select new Person { Name = f.Name, Id = f.Id, Alias = f.Alias }).ToList();
var result = myList.AsEnumerable()
.Select(f => new { F = f, X = DateHelper.RetrieveAge(f.Birthdate))
.Where(f => f.F.age >= 20 && f.F.age <= 40 })
.Select(x => new Person { Name = f.F.Name, Id = f.F.Id, Alias = f.F.Alias })
.ToList();
var result = myList.AsEnumerable().Where(f =>
{
var age = DateHelper.RetrieveAge(f.Birthdate);
return age >= 20 && age <= 40
}).Select(x => new Person { Name = x.Name, Id = x.Id, Alias = x.Alias }).ToList();

Assign values from one list to another using LINQ

Hello I have a little problem with assigning property values from one lists items to anothers. I know i could solve it "the old way" by iterating through both lists etc. but I am looking for more elegant solution using LINQ.
Let's start with the code ...
class SourceType
{
public int Id;
public string Name;
// other properties
}
class DestinationType
{
public int Id;
public string Name;
// other properties
}
List<SourceType> sourceList = new List<SourceType>();
sourceList.Add(new SourceType { Id = 1, Name = "1111" });
sourceList.Add(new SourceType { Id = 2, Name = "2222" });
sourceList.Add(new SourceType { Id = 3, Name = "3333" });
sourceList.Add(new SourceType { Id = 5, Name = "5555" });
List<DestinationType> destinationList = new List<DestinationType>();
destinationList.Add(new DestinationType { Id = 1, Name = null });
destinationList.Add(new DestinationType { Id = 2, Name = null });
destinationList.Add(new DestinationType { Id = 3, Name = null });
destinationList.Add(new DestinationType { Id = 4, Name = null });
I would like to achieve the following:
destinationList should be filled with Names of corresponding entries (by Id) in sourceList
destinationList should not contain entries that are not present in both lists at once (eg. Id: 4,5 should be eliminated) - something like inner join
I would like to avoid creating new destinationList with updated entries because both lists already exist and are very large,
so no "convert" or "select new".
In the end destinationList should contain:
1 "1111"
2 "2222"
3 "3333"
Is there some kind of elegant (one line Lambda? ;) solution to this using LINQ ?
Any help will be greatly appreciated! Thanks!
I would just build up a dictionary and use that:
Dictionary<int, string> map = sourceList.ToDictionary(x => x.Id, x => x.Name);
foreach (var item in destinationList)
if (map.ContainsKey(item.Id))
item.Name = map[item.Id];
destinationList.RemoveAll(x=> x.Name == null);
Hope this will your desired result. First join two list based on key(Id) and then set property value from sourceList.
var result = destinationList.Join(sourceList, d => d.Id, s => s.Id, (d, s) =>
{
d.Name = s.Name;
return d;
}).ToList();
Barring the last requirement of "avoid creating new destinationList" this should work
var newList = destinationList.Join(sourceList, d => d.Id, s => s.Id, (d, s) => s);
To take care of "avoid creating new destinationList", below can be used, which is not any different than looping thru whole list, except that it probably is less verbose.
destinationList.ForEach(d => {
var si = sourceList
.Where(s => s.Id == d.Id)
.FirstOrDefault();
d.Name = si != null ? si.Name : "";
});
destinationList.RemoveAll(d => string.IsNullOrEmpty(d.Name));
Frankly, this is the simplest:
var dictionary = sourceList.ToDictionary(x => x.Id, x => x.Name);
foreach(var item in desitnationList) {
if(dictionary.ContainsKey(item.Id)) {
item.Name = dictionary[item.Id];
}
}
destinationList = destinationList.Where(x => x.Name != null).ToList();
You could do something ugly with Join but I wouldn't bother.
I hope this will be useful for you. At the end, destinationList has the correct data, without creating any new list of any kind.
destinationList.ForEach(x =>
{
SourceType newSource = sourceList.Find(s=>s.Id == x.Id);
if (newSource == null)
{
destinationList.Remove(destinationList.Find(d => d.Id == x.Id));
}
else
{
x.Name = newSource.Name;
}
});

Categories