GroupBy with different parameters - c#

I have one query with groupBy parameters depends on the user input. If the condition is true, query will be grouped with DOB attribute. else, it doesnt need to.
Here is my code
var userList = user.GroupBy(x => new { x.Name, x.Age});
if (isBaby)
{
userList = user.GroupBy(x => new { x.Name, x.Age, x.DOB });
}
but i got an error which the parameters is not same. The latter code is to select from this query like this.
var allList= userList.Select({
...
}).ToList();
I dont want to create two select list because it is easier to manage if only use one select list.
Edited:
This is the error
Cannot implicitly convert type 'System.Collections.Generic.IEnumerable<SystemLinq.IGrouping<<anonymous type: string name, string age>, Domains.User>>' to 'System.Collections.Generic.IEnumerable<SystemLinq.IGrouping<<anonymous type: string name, string age, string DOB>, Domains.User>>'
enter code here

Assuming DOB is a DateTime:
var userList = user.GroupBy(x => new { x.Name, x.Age, dob = isBaby ? x.DOB : DateTime.MinValue });
That way the expression is always the same type, but won't cause an additional tier of grouping when isBaby is false.

Well you are constructing two different results in the group by...
Var implicitly tries to assume the type, and the first assignment is an anon type with only Name and Age. The second one adds DOB which is a different anon type.
Something like
new { x.Name, x.Age, DateOfBirth = isBaby ? x.DOB : DateTime.MinValue }
Would fix it

Related

C# linq how to get property from predicate list when condition is met

I have the following linq query
var usersToNotify = trainingUsers.Where(x => delegatesToBeReminded.Any(d => d.UserGuid == x.UserGuid))
.Select(x => new RecipientDetail
{
FullName = x.FullName,
Email = x.Email,
// get property from delegatesToBeReminded
})
.ToList();
In the example above, i have trainingusers and delegatesToBeReminded list. i want to retrieve the matching record found in trainingusers and create custom type, with fullname, email from trainingusers and additional property from delegatesTobeReminded.
Can anyone help me how to do this?
Can i use something like this?
var x = from tu in trainingUsers
join d in delegatesToBeReminded on tu.UserGuid equals d.UserGuid
select new RecipientDetail
{
FullName = tu.FullName,
Email = tu.Email,
Session = d.Session
};
Thanks
Easiest would be to use a join, as you suggested:
trainingUsers.Join(
delegatesToBeReminded,
user => user.UserGuid,
delegateToBeReminded => delegateToBeReminded.UserGuid,
(user, delegateToBeReminded) => new RecipientDetail
{
FullName = user.FullName,
Email = user.Email,
Delegate = delegateToBeReminded,
});
(Or you can write the equivalent in linq query syntax, as you did).
Another way is to rewrite this in linq query syntax, using let:
from user in trainingUsers
let delegateToBeReminded = delegatesToBeReminded.FirstOrDefault(d => d.UserGuid == user.UserGuid)
where delegateToBeReminded != null
select new RecipientDetail
{
FullName = user.FullName,
Email = user.Email,
Delegate = delegateToBeReminded,
}
Note that these differ depending on what happens if there is more than one delegate for a particular user. The first creates a new RecipientDetail object for each user/delegate pair; the second creates a RecipientDetail object per user, and picks the first delegate.

String parameter having name of column of database to actual column value

I have a method that sends a string parameter(paramType) having name of column of database. and I want to get the value of the column in my Select statement.
I need to replace paramType with value of that column.
var details = DetailsRepository
.Find(application => application.Created > compareDate)
.Select(m => new {m.ApplicationId, paramType })
.Distinct();
I just need to replace the paramType with name of the column is Select. Suppose I have paramtype = "Address", I want the second element to be m.Address.
If the value of paramType is the name of a member, then to do this you'll have to build an expression tree at runtime. This is possible, but is awkward unless all the columns you'll be selecting will always be of the same type.This is further complicated by the fact that you want to select a tuple in the lambda. Frankly, it would be easier to write the SQL dynamically in this case...
Edit: you could try this, which demonstrates the amount of evil needed here:
static Expression<Func<Application, T>> BuildTupleSelector<T>(
string paramType, Func<T> usedForGenericTypeInference)
{
var m = Expression.Parameter(typeof(Application), "m");
var body = Expression.New(typeof(T).GetConstructors().Single(),
new Expression[] {
Expression.PropertyOrField(m, nameof(Application.ApplicationId)),
Expression.PropertyOrField(m, paramType)
},
new MemberInfo[]
{
typeof(T).GetProperty("ApplicationId").GetGetMethod(),
typeof(T).GetProperty("Value").GetGetMethod()
});
return Expression.Lambda<Func<Application, T>>(body, m);
}
with usage:
var details = DetailsRepository
.Find(application => application.Created > compareDate)
.Select(BuildTupleSelector(paramType,
() => new { ApplicationId = 123, Value = "abc" }))
.Distinct();
details should now the the anonymous type with an int ApplicationId and string Value.

How store different data type in list c#

I have a collection of data
id int
Name string
so i want to store this data in a list.
i am writing query
List<int> storedata = tabledata.Select(p => new {p.id, p.Name});
but i am getting error. so what is the right way to do this.
I suggest using var and .ToList() if you want a list of anonymous type:
var store = tabledata
.Select(p => new {id = p.id, name = p.Name}) // Anonymous type
.ToList(); // list of anonymous type's items
Let .Net infer (var) the type for you. However, taking your data into account (id and name), you may want to store the data as a dictionary, not list:
Dictionary<int, string> data = tabledata
.ToDictionary(p => p.id, p => p.Name);
...
string name123 = data[123]; // let's have a value that corresponds to id = 123
if (data.ContainsKey(789)) {
// do we have id = 789?
}
if (data.TryGetValue(456, out var name456)) { // C# 7.0 Syntax
// If we have id = 456, return corresponding value into name456
}
There are a couple compiler errors involved in this line:
List<int> storedata = tabledata.Select(p => new {p.id, p.Name});
First, the Select() method returns an IEnumerable. Trying to store the result of a Select operation into a List just won't work.
Second, the type returned by the Select operation is a problem. The body of the selection...
p => new {p.id, p.Name}
... returns an anonymous type. The storedata variable you've defined as List expects to be populated with simple integers.
Not that I'm recommending it but the following would compile, for example:
IEnumerable<object> storedata = tabledata.Select(p => new {p.id, p.Name});
You are getting an error because the return value of Select(p => new {p.id, p.Name}) is not List<int> but it is of an IEnumerable of anonymous type which is specified by the compiler and cannot be known when you write the code.

Linq queries in asp.net

I have this function below which is used to get those roles which are not already assigned. I have two list now.
One which has all the roles and the other one which has those roles which are already assigned to that user.
How do I return only those which are present in allRoles but not in alreadyHaveRoles?
public dynamic GiveRolesWhichAreNotAssignedToThisUser(String token, String selectedUser, String selectedOrganization)
{
User u = InstaFood.Core.User.Get(selectedUser);
var allRoles = RolesType.GetByOrganizationType(selectedOrganization).Select(i => new
{
ID = i.Id,
Name = i.Name
});
var alreadyHaveRoles = u.GetUserRoles().Select(i => new
{
ID = i.roleTypeId,
Name = ""
});
return ?? // what should be done here?
}
Can I compare them now given that both have same attributes now?
You can use Except to return the difference of two sequences, eg:
var difference=allRoles.Except(alreadyHaveRoles);
This assumes the two sequences contain items of the same type. The objects are checked for equality using their Equals implementation.
If you want to use your own equality comparison, you need to either have your objects implement IEquatable or create a custom EqualityComparer and use the Except overload that accepts a custom EqualityComparer.
In your case, you return two anonymous types that don't even have the same fields. A human would have to guess how to compare the two types, a computer will simply refuse to compile the code. If you consider two items equal if the IDs are equal, simply return the IDs, eg:
var allRoleIds = RolesType.GetByOrganizationType(selectedOrganization)
.Select(i => i.Id);
var assignedRoleIds = u.GetUserRoles().Select(i => i.roleTypeId);
var unassignedRoleIds=allRoleIds.Except(assignedRoleIds);
Retrieving the unassigned roles is trivial after that, just use:
var unassignedRoles=RolesType.GetByOrganizationType(selectedOrganization)
.Where(role=>unassingedRoleIds.Contains(role.Id);
public dynamic GiveRolesWhichAreNotAssignedToThisUser(String token, String selectedUser, String selectedOrganization)
{
User u = InstaFood.Core.User.Get(selectedUser);
var allRoles = RolesType.GetByOrganizationType(selectedOrganization).Select(i => new
{
ID = i.Id,
Name = i.Name
});
var alreadyHaveRoles = u.GetUserRoles().Select(i => new
{
ID = i.roleTypeId,
Name = ""
});
return allRoles.Where(i=>!alreadyHaveRoles.Contains(i));
}
Not tried but it should work

Convert IEnumerable<T> to string[]

i have an entity called Product
class Product
{
public Id { get; set; }
public Name { get; set; }
}
and i have a list of all products:
IEnumerable<Product> products = _productRepository.GetAll()
i want to get an array of strings from this list of products this array will contains the product Id + Product Name, so when i try to cast it using the following code:
string[] s = products.Cast<string>().ToArray();
i got the following exception:
Unable to cast object of type 'Product' to type 'System.String'
the exception really makes alot fo scence, so if i had a method
string ProductToString(Product p)
{
return p.Name;
}
or an override to ToString() for the product object so how i can use this method to get the list of string[] from IEnumerable ?
Well, given that method you can use1:
string[] s = products.Select<string>(ProductToString).ToArray();
However, it would be more idiomatic to do this without a separate method, usually, using a lambda expression:
// Matches ProductToString, but not your description
string[] s = products.Select(p => p.Name).ToArray();
I'd only use a separate method if it was going to be called from various places (ensuring consistency) or did a lot of work.
EDIT: I've just noticed that your description (wanting ID + name) doesn't actually match the ProductToString method you've given (which just gives the name). For the ID + name I'd use:
string[] s = products.Select(p => p.ID + " " + p.Name).ToArray();
or
string[] s = products.Select(p => string.Format("{0} {1}", p.ID, p.Name))
.ToArray();
Or you could just change your ProductToString method, of course.
Alternatively, you could override ToString() in Product, if this is usually how you want to convert a Product to a string. You could then either use a method group conversion or a lambda expression to call ToString.
1 It's possible that you don't need to specify the type argument explicitly - that:
string[] s = products.Select(ProductToString).ToArray();
will work fine - the rules for type inference and method group conversions always confuse me and the compiler behaviour has changed slightly over time. A quick test just now looks like it does work, but there could be subtleties in slightly different situations.
string[] s = products.Select(p => p.Name).ToArray();
Or, if you need Id + Name:
string[] s = products.Select(p => p.Id + ' ' + p.Name).ToArray();
use
string[] s = (from p in products select p.Id.ToString() + " " + p.Name.ToString()).ToArray();
This worked for me:
String.Join(";", mi_customer.Select(a => a.Id).Cast<string>().ToArray());
Where mi_customer must be your object, in my case is a table.

Categories