How to intersect two different object collections by specific property? - c#

There are two different objects.
UserDto.cs
public Guid GradeId { get; set; }
public bool isActiveUser { get; set; }
User.cs
public Guid GradeId { get; set; }
public bool isActiveUser { get; set; }
public Guid ParentUserId { get; set; }
Both object have some data and I am trying to get intersection and update only those objects in DB.
var toBeUpdated = userDtos
.Intersect(mapper.Map<List<User>>(users)) //USING AutoMapper here
.ToList();
foreach (var item in toBeUpdated)
{
userRepository.Update(item);
}
These objects must be intersected with GradeId. With the code I wrote, I am not getting any objects by that query.
Example :
users :
GradeId - 1
isActive - false
userDtos :
GradId - 1
isActive - true
I want to be able to intersect these two collections by GradeId and set isActive to true(as in DTO)

There are several ways how this can be done. The most intuitive way to find intersections of collections is to iterate over both collections in a double loop:
foreach (var dto in userDtos)
{
foreach (var user in users)
{
if (user.GradeId == dto.GradeId)
{
// Do something work here...
userRepository.Update(user);
}
}
}
You can also do the same using Linq:
IEnumerable<User> usersToUpdate =
from dto to userDtos
from user to users
where user.GradeId == dto.GradeId
select user;
foreach (var user in usersToUpdate)
{
// Do something work here...
userRepository.Update(user);
}
Or like this:
IEnumerable<User> usersToUpdate = userDtos
.SelectMany(dto => users, (dto, user) => new { dto, user })
.Where(pair => pair.user.GradeId == pair.dto.GradeId)
.Select(pair => pair.user);
foreach (var user in usersToUpdate)
{
// Do something work here...
userRepository.Update(user);
}

There is no need to use nested Foreach.
var items = userDtos.Select(x => x.GradeId)
.Intersect(users.Select(y => y.GradeId))
.ToList();

Related

Is there a good way to dissolve a list on itself through linq

I'm searching for a way to dissolve a list with groups and users on itself. The problem is a group can be a result of only users, of other groups or even both. If a group is already in the list (and the all should be), I want to join the users. The goal should be list without subgroups and all groups should contain only users.
For example: There is a group called "All employees" consisting of subgroups with users. I need all users in the group "All employees" instead of the subgroups. I've already tried something like the code below. Is there a better way to do this? Because my code doesn't dissolve all subgroups.
foreach (var g in Usergroup)
{
if (g.groups != null)
{
foreach (var t in g.groups)
{
foreach (var z in Usergroup)
{
if (z.Name == t.Name)
{
if (g.Users != null && z.Users != null)
{
UserMapping[] tmp = new UserMapping[g.Users.Length + z.Users.Length];
g.Users.CopyTo(tmp, 0);
z.Users.CopyTo(tmp, g.Users.Length);
g.Users = tmp.GroupBy(p => p.Name).Select(grp => grp.First()).ToArray();
}
}
}
}
}
}
internal class GroupUserClassMapping
{
public string Name { get; set; }
public UserMapping[] Users { get; set; }
public GroupsMapping[] groups { get; set; }
}
Thanks a lot!
Regards The Dentist

Unable to get product names based on condition

I have two model classes
public class GetProductNameRequest
{
public DateTime ExpiryDate { get; set; }
public bool IsActive { get; set; }
}
public class GetProductNameResponse
{
public List<string> ProductName { get; set; }
}
linq query:
public async Task<List<ProductSKU>> GetProductNames(GetProductNameRequest request)
{
var res = DbSet.Include(t => t.Product).Where(t => t.EndDate >= request.ExpiryDate && t.IsActive == request.IsActive).GroupBy(x => x.Product);
Contracts.Models.ProductNameResponse product = new Contracts.Models.ProductNameResponse();
foreach (var item in res)
{
product.ProductName = item.Key.ProductName;
}
}
So i'm unble get list of Product Names based on Id's plz let me know the solution.
productsku table:
SkuId ProductId Sku MRP CreatedOn UpdatedOn StartDate EndDate IsActive RowVersion
You have a number of problems in your code. Two most obvious reasons why you don't get anything in your product variable is that it is initialized inside the loop, and nothing gets added to it. The code should be something like
Contracts.Models.ProductNameResponse product = new Contracts.Models.ProductNameResponse();
foreach (var item in res)
{
product.ProductName.Add(item.Key.ProductName);
}
I also think your LINQ statement will still throw an error about one cursor not close while another one is open. Search for IQueryable vs IEnumerable issue. But you don't list that as a problem; so maybe in your data source it is fine.

Update a value with linq

I'm trying to update the Selectedproperty of an IEnumerable<SelectListItem> for a MVC-Combobox website using linq. However this is not working, as shown in the debbuging result: The Count() for the criteria returns an item, however the Count()for .Selected == truereturns 0.
public IEnumerable<SelectListItem> Categories { get; set; }
public CategoryModel Category
{
get { return category; }
set
{
category = value;
Categories.Where(x => x.Value == value.Id.ToString()).First().Selected = true;
}
//Debugging Results
//?Categories.Where(x => x.Value == value.Id.ToString()).Count()
//1
//?Categories.Count(x => x.Selected == true);
//0
}
Update:
I guess the problem is more bound to the IEnumerable<SelectListItem>, because the after changing Categories to an ObservableCollection it works fine (example below), even though LinQ is not designed for changing data....
System.Diagnostics.Debug.Print(Categories.Where(x => x.Id == value.Id).FirstOrDefault().Description);
Categories.Where(x => x.Id == value.Id).FirstOrDefault().Description = "Stackoverflow";
System.Diagnostics.Debug.Print(Categories.Where(x => x.Id == value.Id).FirstOrDefault().Description);
LINQ is to query your data-source not to modify it.
Your current approach has a drawback anyway, you would select one but you would not deselect the others. So you need a loop:
public CategoryModel Category
{
get { return category; }
set
{
category = value;
// consider to use a lock here to avoid multi threading issues
foreach(SelectListItem catItem in Categories)
catItem.Selected = catItem.Value == value.Id.ToString();
}
}
I would use a method SetSelectedCategory instead of a property if i'd modify a collection.
IEnumerable does not guarantee that changes get persisted across enumerations.
It all depends on the underlying implementation in the end (List, Array, Observable, etc).
Among the options that you have is to change your actual Categories to a writable collection (like List)...
But you might not be able to do that, or you might simply prefer to stay lean and keep using the IEnumerable.
In that case you could simply mutate the original collection and project it over the original
void Main()
{
Categories = Load();
var active = new Func<CategoryModel, int, CategoryModel>((category, match) =>
{
return new CategoryModel
{
Id = category.Id,
Name = category.Name,
Active = category.Id == match
};
});
Categories = Categories.Select(p => active(p, 2));
Categories.Dump();
}
public IEnumerable<CategoryModel> Categories { get; set; }
public IEnumerable<CategoryModel> Load()
{
yield return new CategoryModel { Id=1, Name = "one" };
yield return new CategoryModel { Id=2, Name = "two" };
yield return new CategoryModel { Id=3, Name = "three" };
}
public class CategoryModel
{
public int Id { get; set; }
public string Name { get; set; }
public bool Active { get; set; }
}
Id|Name|Active
1 one False
2 two True
3 three False
this is also to emphasize that you can use linq for "transformations" using "projections"

How to order Nested Collections in Linq and EF

i would like to make a treelistview for my Data.
Tree should look like this
Accounts
-> Providers
-> Accounts
public sealed class AccountRoot
{
public AccountRoot()
{
Providers = new Collection<Hoster>();
}
public long AccountRootId { get; set; }
public ICollection<Hoster> Providers { get; set; }
}
public sealed class Hoster
{
public Hoster()
{
Accounts = new Collection<Account>();
}
[Key]
public long HosterId { get; set; }
public long AccountRootId { get; set; }
public string Name { get; set; }
public ICollection<Account> Accounts { get; set; }
}
public sealed class Account
{
[Key]
public long AccountId { get; set; }
public long HosterId { get; set; }
public Hoster Hoster { get; set; }
public string Name { get; set; }
}
I would like to order my query.
should be sth like
Accounts
Providers A-Z
Accounts A-Z
what i got until now is..
var query = _entity.AccountRoot.Local
.Select(x => new AccountRoot()
{
AccountRootId = x.AccountRootId,
Providers = x.Providers.OrderBy(y => y.Name).ToList()
}).ToList();
What is missing is the orderby for the next nested collection.
Thank you for your help ! :-)
It can be a bit different approaches depending on if you already have a result set, and want to just sort it in code, or if you want to construct IQueryable<> for EF which will be successfully compiled to SQL and executed with actual sorting in database.
First, assume you already have the collection in code. In this case, you have object AccountRoot, which contains collection of Providers, each of which has collection of Accounts. Obviously, you cannot return the same objects, as you need to reorder collection properties, so all you need is to just construct new ones. I would just sort the collections, but you could construct completely new entities, if you need:
var query = ...
.Select(x => new AccountRoot
{
// add copy properties here
// ....
Providers = x.Providers
.Select(y =>
{
// Here we can construct completely new entity,
// with copying all properties one by one,
// or just reorder existing collection as I do here
var result = y;
result.Accounts = y.Accounts.OrderBy(z => z.Name).ToArray();
return result;
})
.OrderBy(y => y.Name)
.ToArray()
})
.ToArray();
Second case, if you need to get it directly from SQL, is a bit different, as you cannot use all that var result = ...; ... return result stuff in lambda - it won't compile to SQL. But idea is the same - you need to construct projection from data sets. It should be something like this:
var query = ...
.Select(x => new AccountRoot
{
AccountRootId = x.AccountRootId,
// Other properties to copy
// ...
Providers = x.Providers
.Select(y => new Hoster
{
HosterId = y.HosterId,
// Other properties to copy
// ...
Accounts = y.Accounts.OrderBy(z => z.Name).ToArray(),
})
.OrderBy(y => y.Name)
.ToArray()
})
.ToArray();

Get items for list

I got the following documents:
public class TreeNode
{
string Id;
string Owner; //"users/1"
string TodoListId; //"todolists/1"
string ParentId; //"treenodes/1"
}
public class TodoList
{
string Id;
List<TodoItem> Items;
}
public class TodoListItem
{
bool IsCompleted;
}
How can I fetch all items for the current user which has not completed? Should I redesign any of the documents?
I want something like:
from all treenodes belonging to the current user
load all todolists
and return all active items within those lists
But within one server roundtrip
Update 2
Here is how I tried to do it with two queries (SelectMany is not supported):
var todoListIds = _dbSession.Query<UserTreeNode>()
.Where(x => x.UserId == user.Id)
.Select(x => x.TodolistId);
var nodes = _dbSession.Query<Todolist>()
.Where(x => x.Id.In(todoListIds))
.SelectMany(x => x.Items.Where(item => !item.IsCompleted));
You can't make RavenDB only return a sub-set of a single doc, so in your case you need to get the entire TodoList and then just filter on the client.
You can do this in a single network call using the Include feature, this should work:
var todoListIds = _dbSession.Query<UserTreeNode>()
.Include(x => x.TodoListId)
.Where(x => x.UserId == user.Id)
.Select(x => x.TodolistId);
foreach (var userListId in todoLisIds)
{
//This won't cause an extra network call, because the session will have already loaded it
var todoList = _dbSession.Load<TodoList>(userListId);
//In-memory filtering to get the outstanding items
var activeItems = todoList.Items.Where(x => x.IsCompleted).ToList();
}
I think what you have provided is not the real code but the following gives you uncompleted items from a todolist object.
list.Items.Where(q => q.IsCompleted == false);
I spent some time on it and I believe you need a different approach, (please note its something related to architecture and I can't be 100% sure about it, it might need some modification). It seems like you want to create a TODO List for the user. I think, its may be better if you could structure it in a way That
A User Can have one or more To Do List(s) (I have assumed one To Do List in my example)
One To Do List Item can have multiple instances of Actual work to Do
I would follow a structure similar to below:
public class User
{
public string ID { get; set; }//.... All User Attributes AND
}
public class TodoList
{
public string Id { get; set; }
public User owner { get; set; }
}
public class TodoListItem
{
public string ItemID { get; set; }
public TodoList parent { get; set; }
public string ItemDescription { get; set; }
public bool IsCompleted { get; set; }
}
Above I have a class for User which is currently representing your user. Then I have a class for ToDoList which is holding User class object (not the id of user) then I have ToDoListItem which is holding ToDoList object as parent.
If we look through database perspective than we have One to Many relationship between User and ToDoList and again one to many in ToDoList and ToDoListItem.
Now if you want to search user with incomplete work to do , just try the following linq query:
var query = from t in listTDL
where t.IsCompleted == false
select t.parent.owner;
You might need these lines to fill a test data structure:
User user = new User() { ID = "User1" };
TodoList td = new TodoList() { Id = "TD1", owner = user};
List<TodoListItem> listTDL = new List<TodoListItem>();
TodoListItem tdl = new TodoListItem() { ItemID = "TDL1", ItemDescription = "Frist Item", IsCompleted = false, parent=td };
listTDL.Add(tdl);
listTDL.Add(new TodoListItem() { ItemID = "TDL2", ItemDescription = "second Item", IsCompleted = true, parent = td });
listTDL.Add(new TodoListItem() { ItemID = "TDL3", ItemDescription = "third Item", IsCompleted = true, parent = td });
listTDL.Add(new TodoListItem() { ItemID = "TDL4", ItemDescription = "fourth Item", IsCompleted = false, parent = td });
List<User> userList = new List<User>();
userList.Add(user);
Here is how I would do:
var result = todoList.Where
(
x => nodeList.Any
(
y => y.Owner == "ownerId" && y.TodoListId == x.Id
)
).SelectMany(x => x.Items).Where(z => !z.IsCompleted);
P.s. I'am not familiar with RavenDB so showing just an idea

Categories