Dynamic Linq query with multiple Where Average - entity-framework

I can get my average rating value for specific result like below:
var result1 = items.Where(x => x.Ratings.Average(y => y.MyRate == 1));
var result2 = items.Where(x => x.Ratings.Average(y => y.MyRate == 2));
var result3 = items.Where(x => x.Ratings.Average(y => y.MyRate == 3));
...
So result1 contains only items with avg rating is 1
So result2 contains only items with avg rating is 2
So result3 contains only items with avg rating is 3
...
Now, I would like to be able to combine these queries. I mean for example to get items with avg rating of 2, 3 and 4 at the same time.
var results = items.Where(x => x.Ratings.Average(y => y.MyRate == 2 ||
x.Ratings.Average(y => y.MyRate == 3 ||
x.Ratings.Average(y => y.MyRate == 4));
The solution above is not dynamic because 2, 3, 4 are fix.
So let's say I have variable ratings which is a list of rating values. How can I query all items with avg rating contained in this variable ?
List<int> ratings = new List<int>() { 2, 3, 4};
var results = items.Where(x => x.Ratings.Average(y => y.MyRate in ratings)); <--- does not work

Tested using Linq Contains
List<decimal> ratings = new List<decimal>() { 1, 2, 3, 4 };
var result = items.Where(x => ratings.Contains(x.Ratings.Average(y => y.MyRate)));
Heres the classes I used for testing. The above query contains Toy Story 2 and 3 but not 4 as Toy Story 4's average is not in ratings
class Item
{
public string Name { get; set; }
public ICollection<Rating> Ratings { get; set; }
}
class Rating
{
public decimal MyRate { get; set; }
}
With the following initialization code
var items = new List<Item>
{
new Item //MyRate average = 1
{
Name = "Toy Story 2",
Ratings = new List<Rating>
{
new Rating { MyRate = 1 },
new Rating { MyRate = 1 },
new Rating { MyRate = 1 }
}
},
new Item //MyRate average = 2
{
Name = "Toy Story 3",
Ratings = new List<Rating>
{
new Rating { MyRate = 1 },
new Rating { MyRate = 2 },
new Rating { MyRate = 3 }
}
},
new Item //MyRate average = 1.33
{
Name = "Toy Story 4",
Ratings = new List<Rating>
{
new Rating { MyRate = 1 },
new Rating { MyRate = 1 },
new Rating { MyRate = 2 }
}
}
};

Related

Prioritizing Constraints in linear SolverContext Model using SolverFoundation

Good morning,
I am trying to solve what I believe is a linear problem, using Microsofts SolverFoundation in code (c#). I see most references to this type of solving are related to the Solver inside of Excel, and it indeed does share many similarities. However, I have written up an example of what I am trying to do.
For this example, lets say I have 3 grocery stores (Albertsons, Safeway, Costco), and 3 different types of apples (Red, Green, Fuji).
public enum AppleType
{
Red,
Green,
Fuji,
}
Each of these stores offers these different apple types at different percentages per order.
public class Store
{
public string Name { get; set; }
public List<Apple> ApplesOffered { get; set; }
}
public class Apple
{
public AppleType AppleType { get; set; }
public double OrderPercent { get; set; }
}
Here is my mock-data for this setup.
List<Store> stores = new List<Store>
{
new Store()
{
Name = "Albertsons",
ApplesOffered = new List<Apple>
{
new Apple(){AppleType = AppleType.Red, OrderPercent = 80 },
new Apple(){AppleType = AppleType.Green, OrderPercent = 15 },
new Apple(){AppleType = AppleType.Fuji, OrderPercent = 0 }
}
},
new Store()
{
Name = "Safeway",
ApplesOffered = new List<Apple>
{
new Apple(){AppleType = AppleType.Red, OrderPercent = 12 },
new Apple(){AppleType = AppleType.Green, OrderPercent = 30 },
new Apple(){AppleType = AppleType.Fuji, OrderPercent = 0 }
}
},
new Store()
{
Name = "Costco",
ApplesOffered = new List<Apple>
{
new Apple(){AppleType = AppleType.Red, OrderPercent = 10 },
new Apple(){AppleType = AppleType.Green, OrderPercent = 35 },
new Apple(){AppleType = AppleType.Fuji, OrderPercent = 40 }
}
}
};
Alright, so say I have a list of apple types and I want to choose how many of each I want, and the solver should give me the minimal optimized orders from different stores to get what I want.
my code for building the constraints is as follows:
var context = SolverContext.GetContext();
context.ClearModel();
var model = context.CreateModel();
// Decisions
stores.ForEach(store => model.AddDecisions(new Decision(Domain.RealNonnegative, store.Name)));
// Constraints
var constraints = new List<CustomAppleConstraint>();
stores.ForEach(store =>
{
foreach (AppleType a in (AppleType[])Enum.GetValues(typeof(AppleType)))
{
var ao = store.ApplesOffered.FirstOrDefault(_ => _.AppleType == a);
if (ao != null && ao.OrderPercent > 0)
{
constraints.Add(new CustomAppleConstraint
{
Type = a,
Value = $"0.{ao.OrderPercent} * {store.Name}"
});
}
}
});
// Add Constraints to model
var constraintGroups = constraints.GroupBy(_ => _.Type).ToList();
foreach (AppleType a in (AppleType[])Enum.GetValues(typeof(AppleType)))
{
var group = constraintGroups.FirstOrDefault(_ => _.Key == a);
if (group != null)
{
model.AddConstraint($"_{a}", $"{(string.Join(" + ", group.Select(_ => _.Value).ToArray()))} >= {order[a]}");
}
}
// Solve
var solution = context.Solve(new SimplexDirective());
var solutionResults = new List<KeyValuePair<string, double>>();
foreach (var decision in solution.Decisions)
{
var value = (double)decision.GetValues().First()[0];
solutionResults.Add(new KeyValuePair<string, double>(decision.Name, value));
}
return solutionResults;
I have tested this with a bunch of different orders, and it all appears to be giving me the correct data.
Now say I have the following simple order where I only want green apples:
{AppleType.Red, 0},
{AppleType.Green, 10},
{AppleType.Fuji, 0}
I get back a result suggesting 28.57 orders from Costco, which I would expect because Costco offers the highest percentage of green apples per order.
So here is where I am trying to figure the correct way to implement one more constraint.
Say I have some preferences for which stores I use to get certain apple types. (eg, I only want my green apples from Safeway).
// Apple Type : Preferred Store
var orderPrefs = new Dictionary<AppleType, string>()
{
{AppleType.Red, "Albertsons"},
{AppleType.Green, "Safeway" },
{AppleType.Fuji, "Costco" }
};
So even though Costco provides the highest percentage of green apples, I want to add some constraints from my preferences that prioritize the green apple result from Safeway. In this instance, my prefs say I Only want green apple from Safeway, so 100% of this order should come from Safeway.
Granted this is a very simple example, but its the meat of what I am trying to wrap my head around.
I hope this makes sense... I have been trying to figure this out for a few days now with no luck. Thank you.

Getting all months in an Year from Entity Framework core query

I have done following EF.core(3.1/3.2) query
var monthlySalesList = context.Bids.Include(r => r.AllRequest).ThenInclude(r => r.Category).Where(b => b.UID== UID && (b.Status == MyStatus.Awarded || b.Status == MyStatus.Completed))
.GroupBy(a => new { Month =a.InsertedDate.Month })
.Select(g => new MyServiceList()
{
Key = g.Key.ToString(),
Month = g.Key.Month.ToString(),
Total= g.Sum(s => s.totalBudget)
}).ToList();
I am not getting all months in an year instead it displays only 2 months say ( 10,11) with total.In above query Mystatus is an ENUM class and MyserviceList Model class contains get & set such as key,month,sum and total .
I am getting only
-----------------
Months total
------------------
10 1234
11 1212
How can I get remaining months with zero value.
-----------------
Months total
------------------
1 0
2 0
3 0
4 0
5 0
6 0
7 0
8 0
9 0
10 1234
11 1212
12 0
Define another list
Make a for loop after "monthlySalesList" and make query from the list.
if you did not get data, set 0 on new list, otherwise set value .
for example :
Class :
public class ReportData
{
public int month { get; set; }
public int total { get; set; }
}
Code :
List reportData = new List();
for (int i = 1; i <= 12; i++)
{
try
{
Bids bids = new Bids();
bids = monthlySalesList.Where(t => t.month == i).FirstOrDefault();
if(bids == null)
{
reportData.Add(new ReportData() {
month = i,
total = 0
});
}
else
{
reportData.Add(new ReportData()
{
month = i,
total = bids.total
});
}
}
catch(Exception ex)
{
}
}

Filters not working in my repository in ASP.NET Core

I have these parameters in a class:
public class UserParams
{
public string Gender {get; set;}
public int MinAge {get; set;} = 1;
public int MaxAge {get; set;} = 19;
}
The query is done in the repository as shown below. First is to query for the child sex or gender and the second is to query for the child sex or gender
var query = _context.Children.AsQueryable();
query = query.Where(c => c.Sex == userParams.Gender);
var minchildDob = DateTime.Today.AddYears(-userParams.MaxAge - 1);
var maxchildDob = DateTime.Today.AddYears(-userParams.MinAge);
query = query.Where(u => u.DateOfBirth >= minchildDob && u.DateOfBirth <= maxchildDob);
return await PagedList<Child>.CreateAsync(query.AsNoTracking(), userParams.PageNumber, userParams.PageSize);
The gender filter returns empty array of children and the minchildDob and maxchildDob too not working
if (!string.IsNullOrEmpty(temp.Gender))
{
all = all.Where(u => new[] { "men", "women" }.Contains(u.sex));
//all = all.Where(t => t.sex == temp.Gender);
}
=======================Update=======================
var temp = new UserParams();
temp.Gender = "men";
var minchildDob = DateTime.Today.AddYears(-temp.MaxAge - 1);
var maxchildDob = DateTime.Today.AddYears(-temp.MinAge);
IEnumerable<Table> all = from m in _context.data
select m;
_logger.LogError("all data");
foreach (var item in all)
{
_logger.LogError(item.name);
}
_logger.LogError("============================================");
if (!string.IsNullOrEmpty(temp.Gender)) {
all = all.Where(t => t.sex == temp.Gender);
}
_logger.LogError("filter gender");
foreach (var item in all) {
_logger.LogError(item.name);
}
_logger.LogError("============================================");
if (temp.MaxAge > 0) {
all = all.Where(t => t.birthday >= minchildDob && t.birthday <= maxchildDob);
}
_logger.LogError("filter age");
foreach (var item in all)
{
_logger.LogError(item.name);
}
_logger.LogError("============================================");

Dynamic list using array from anthor list

My application is ASP.NET MVC 5 / SQL Server.
I am trying to select specific columns from a list based on an array:
First list has 200 columns: Age, Gender, .....
var list1 = _reportRepository.ShowMasteView().ToList();
Second list has 20 columns: Age, Gender, ......
From the view I select the items to be displayed:
string[] lits2 = showColumn.Where(c => c.Value == true).Select(c=> c.Key).ToArray();
I get
To get these two specific columns, I tried
var nList = list1.Select(t2 => lits2.Any(t1 => t2.Contains(t1)));
I get an error
Can not resolve symbol "Contains"
I was able to do it using the following
var keys = "Age,Gender";
var connection =
ConfigurationManager.ConnectionStrings["DALEntities"].ConnectionString;
using (var dataAdapter = new SqlDataAdapter("SELECT " + keys
+ " from dbo.vw_MasterView", connection))
{
var dataTable = new DataTable();
dataAdapter.Fill(dataTable);
dataAdapter.FillSchema(dataTable, SchemaType.Mapped);
return dataTable;
}
Is there a better way in linq?
From my understand it appears you are trying to extract/select a dynamic object that only has the desired properties/columns.
This can be achieved by building a dynamic expression/function to apply to the Select
The following builds an expression based on the model type and the provided properties
static class DynamicExtensions {
public static IQueryable<dynamic> SelectDynamic<TModel>(this IQueryable<TModel> query, ISet<string> propertyNames) {
var selector = query.BuildSelectorFor(propertyNames);
return query.Select(selector);
}
static Expression<Func<TModel, dynamic>> BuildSelectorFor<TModel>(this IQueryable<TModel> query, ISet<string> propertyNames) {
var modelType = typeof(TModel);
var properties = modelType.GetProperties().Where(p => propertyNames.Contains(p.Name));
// Manually build the expression tree for
// the lambda expression v => new { PropertyName = v.PropertyName, ... }
// (TModel v) =>
var parameter = Expression.Parameter(modelType, "v");
// v.PropertyName
var members = properties.Select(p => Expression.PropertyOrField(parameter, p.Name));
var addMethod = typeof(IDictionary<string, object>).GetMethod(
"Add", new Type[] { typeof(string), typeof(object) });
// { { "PropertyName", v.PropertyName}, ... }
var elementInits = members.Select(m =>
Expression.ElementInit(addMethod, Expression.Constant(m.Member.Name), Expression.Convert(m, typeof(object))));
// new ExpandoObject()
var newExpando = Expression.New(typeof(ExpandoObject));
// new ExpandoObject() { { "PropertyName", v.PropertyName}, ... }
var expando = Expression.ListInit(newExpando, elementInits);
// (TModel v) => new ExpandoObject() { { "PropertyName", v.PropertyName}, ... }
var lambdaExpression = Expression.Lambda<Func<TModel, dynamic>>(expando, parameter);
return lambdaExpression;
}
}
This takes advantage of ExpandoObject whose members can be dynamically added and removed at run time.
The following test was used as an example of how the above function is invoked.
[TestMethod]
public void DynamicList() {
var list1 = new List<Person>
{
new Person{ Gender = "Male", Age = 10, FirstName = "Nama1", SampleNumber = 12},
new Person{ Gender = "Male", Age = 12, FirstName = "Nama2", SampleNumber = 13},
new Person{ Gender = "Female", Age = 13, FirstName = "Nama3", SampleNumber = 14},
new Person{ Gender = "Male", Age = 14, FirstName = "Nama4", SampleNumber = 15},
};
var keys = new string[] { "Age", "Gender", };
var nList = list1.AsQueryable().SelectDynamic(new HashSet<string>(keys));
foreach (IDictionary<string, object> row in nList) {
var msg = $"{{ {keys[0]} = {row[keys[0]]}, {keys[1]} = {row[keys[1]]} }}";
Debug.WriteLine(msg);
}
}
and produces the following output
{ Age = 10, Gender = Male }
{ Age = 12, Gender = Male }
{ Age = 13, Gender = Female }
{ Age = 14, Gender = Male }
The dynamic objects can be used in the View and it is a simple matter of calling the desired members.
For example suppose you have a model as follows
public class MyViewModel {
public string MyProperty { get; set; }
public string[] Keys { get; set; }
public List<dynamic> MyDynamicProperty { get; set; }
}
that was populated with data and given to the view
var list1 = _reportRepository.ShowMasteView();
var keys = new string[] { "Age", "Gender", };
var nList = list1.AsQueryable().SelectDynamic(new HashSet<string>(keys));
var viewModel = new MyViewModel {
MyProperty = "Hello World",
MyDynamicProperty = nList.ToList(),
Keys = keys
};
return View(viewModel);
Then in the view you can use the model as desired, casting to get access to members in the expando object.
#model MyViewModel
...
<h2>#Model.MyProperty</h2>
<table>
<tr>
#foreach(string key in Model.Keys) {
<th>#key</th>
}
</tr>
#foreach (IDictionary<string, object> row in Model.MyDynamicProperty) {
<tr>
#foreach(string key in Model.Keys) {
<td>#row[#key]</td>
}
</tr>
}
</table>
I think you just need to use Contains on your list2.
var nList = list1.Where(t => lits2.Contains(t1));
Contains is a method for Lists. The code you had was trying to use it on a string.
If you have two list of a person's class
public class Person
{
public int id { get; set; }
public string name { get; set; }
}
If the lists are as below:
var list1 = new List<Person>
{
new Person{ id = 1, name = "Nama1"},
new Person{ id = 2, name = "Nama2"},
new Person{ id = 3, name = "Nama3"},
new Person{ id = 4, name = "Nama4"},
};
var list2 = new List<Person>
{
new Person{ id = 1, name = "Nama1"},
new Person{ id = 2, name = "Nama2"},
};
You can filter in the following ways
var keys = list2.Select(x => x.id).ToList();
var filter1= list1.Where(x => keys.Contains(x.id)).ToList();
var filter2= list1.Where(x => keys.Contains(x.id)).Select(x => new { x.name }).ToList();
var filter3= list1.Select(x => new
{
id = x.id,
name = x.name,
check = keys.Contains(x.id)
}).Where(x => x.check).ToList();
If you have array of string
you can use below code
array string same
var lis1 = new string[] {"name1", "name2","name3" };
var lis2 = new string[] { "name1" };
You can filter array of string in the following ways
var items1= lis1.Where(x=>lis2.Contains(x)).ToList();
var items= lis1.Select(x=> new { x, check= lis2.Contains(x) }).Where(x=>x.check == true).ToList();

How to combine GroupedObservables in rx.net?

I have one observable that I use GroupBy on to get a number of streams. I actually want a Scan result over each sub-stream. Let's say the observable is over product prices and the scan result is average price per product type.
I have another stream of events pertaining to those 'products' (let's say "show product price" events) and I want to combine it with the previous stream's latest product price. So the Scan output per group needs to be combined with each element of the event stream to get the latest average price for that event's product.
For some reason I cannot get the right syntax and I have been bashing away at this all day. Can someone please help?
Update
I am adding the code below to illustrate the approximate intent.
public class Node
{
private List<int> Details = new List<int>();
public void AddInfo(int x)
{
Details.Add(x );
}
public Node(int x)
{
Details.Add(x);
}
public int Index => Details[0]%10; //just to simplify the grouping and debugging
public int Latest => Details.Last();
}
public class Message
{
private static Random _random = new Random();
public int MessageNodeInfo { get; private set; }
public Message()
{
MessageNodeInfo = _random.Next();
}
}
public class AccumulatingInfoTest
{
private static Random _random=new Random();
private IObservable<Message> MessageStream()
{
TimeSpan timeSpan = TimeSpan.FromSeconds(0.5);
var ret= Observable.Generate(0,
_ => { return true; },
_ => { return 0; },
_ => { return new Message(); },
_=> timeSpan)
.Publish()
.RefCount();
return ret;
}
public class ArbitraryCommonClass
{
public int K { get; set; }
public Message M { get; set; }
public Node D { get; set; }
public ArbitraryCommonClass Combine(ArbitraryCommonClass a)
{
return new ArbitraryCommonClass()
{
K = this.K,
M = this.M ?? a.M,
D = this.D ?? a.D
};
}
}
public void Start()
{
var inputStream = MessageStream();
inputStream.Subscribe(y => Console.WriteLine("Input: K " + y.MessageNodeInfo % 10 + " V " + y.MessageNodeInfo));
var nodeInfoStream = inputStream
.Select(nodeInfo => new Node(nodeInfo.MessageNodeInfo))
.GroupBy(node => node.Index)
.Select(groupedObservable => new
{
Key = groupedObservable.Key,
Observable = groupedObservable
.Scan(
(nodeAcc, node) => { nodeAcc.AddInfo(node.Latest); return nodeAcc; }
)
.Select(a => new ArbitraryCommonClass() { K = a.Index, M = (Message)null, D = a })
}
);
var groupedMessageStream =
inputStream
.GroupBy(
m => new Node(m.MessageNodeInfo).Index
)
.Select(a => new
{
Key =a.Key,
Observable = a.Select(b => new ArbitraryCommonClass() { K = a.Key, M = b, D = null })
});
var combinedStreams = nodeInfoStream
.Merge(groupedMessageStream)
.GroupBy(s => s.Key)
.Select(grp => grp
.Scan(
(state, next) => new { Key = state.Key, Observable = Observable.CombineLatest(state.Observable, next.Observable, (x, y) => { return x.Combine(y); }) }
)
)
.Merge()
.SelectMany(x => x.Observable.Select(a=>a));
combinedStreams.Where(x=>x.M!=null).Subscribe(x => Console.WriteLine(x.K + " " + x.M.MessageNodeInfo + " " + x.D.Latest));
}
}
Assuming the following class:
public class Product
{
public string Type { get; set; } = "Default";
public decimal Price { get; set; }
}
Here's a use of GroupBy with Scan (shows the average product price grouped by type). The trick is to Select over the grouped observable to get to the individual groupings, do whatever, then (presumably) merge them back together. You could collapse the Select and the Merge into a single SelectMany, but it can be easier to read when separated:
var productSubject = new Subject<Product>();
var printSignal = new Subject<Unit>();
var latestAverages = productSubject.GroupBy(p => p.Type)
.Select(g => g
.Scan((0, 0.0m), (state, item) => (state.Item1 + 1, state.Item2 + item.Price)) //hold in state the count and the running total for each group
.Select(t => (g.Key, t.Item2 / t.Item1)) //divide to get the average
)
.Merge()
.Scan(ImmutableDictionary<string, decimal>.Empty, (state, t) => state.SetItem(t.Key, t.Item2)); //Finally, cache the average by group.
printSignal.WithLatestFrom(latestAverages, (_, d) => d)
.Subscribe(avgs =>
{
foreach (var avg in avgs)
{
Console.WriteLine($"ProductType: {avg.Key}. Average: {avg.Value}");
}
Console.WriteLine();
});
var productsList = new List<Product>()
{
new Product { Price = 1.00m },
new Product { Price = 2.00m },
new Product { Price = 3.00m },
new Product { Price = 2.00m, Type = "Alternate" },
new Product { Price = 4.00m, Type = "Alternate" },
new Product { Price = 6.00m, Type = "Alternate" },
};
productsList.ForEach(p => productSubject.OnNext(p));
printSignal.OnNext(Unit.Default);
productSubject.OnNext(new Product { Price = 4.0m });
printSignal.OnNext(Unit.Default);
productSubject.OnNext(new Product { Price = 8.0m, Type = "Alternate" });
printSignal.OnNext(Unit.Default);
This uses nuget package System.Collections.Immutable.