Update a value with linq - c#

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"

Related

C# Linq OrderBy Object.ID && Object.SortedList.Key.Value

I'm having headache trying to filter a List, and can't find answer on the web. To be honest, I don't even know if it's possible to achieve, but I guess it should be.
So, MyItem class looks like this :
public class MyItem
{
private int _ID1, _ID2, _registeredTime;
private SortedList<DateTime, AnotherItem> _mySortedList;
public int ID1
{
get { return _ID1; }
set { _ID1 = value; }
}
public int ID2
{
get { return _ID2; }
set { _ID2 = value; }
}
public int RegisteredTime
{
get { return _registeredTime; }
set { _registeredTime = value; }
}
public SortedList<DateTime, AnotherItem> MySortedList
{
get { return _mySortedList; }
set { _mySortedList = value; }
}
}
My goal is to only get the first occurrence of each pair (ID1, ID2) based on RegisteredTime, so I'm using the code below and it works like a charm :
BaseList = ItemsList.OrderBy(x => x.RegisteredTime).ToList();
FilteredList = new List<MyItem>(BaseList.GroupBy(x => new { x.ID1, x.ID2 })
.Select(x => x.First())).ToList());
But another constraint makes me need to get a list based on first pair (ID1, ID2) + MySortedList.Key, so I could get something like :
ID1, ID2, MySortedList[Date1]
ID1, ID2, MySortedList[Date2]
So I'd like to have a request that does :
FilteredList = BaseList.GroupBy(x => new { x.ID1, x.ID2, x.MySortedList."UniqueKey" })
.Select(x => x.First()))
.ToList();
But I can't find a solution to achieve this. I'm kinda new to Linq and don't know how this kind of particularity works (and, as I said before, if it's even possible).
Thanks for your help !
First group the items by the keys, order items of each group by the RegisteredTime and finally retrieve only the first in each group:
var result = BaseList.GroupBy(x => new { x.ID1, x.ID2, x.MySortedList[yourKey]})
.Select(g => g.OrderBy(i => i.RegisteredTime).First());
Notice that if you try to group by a key that is not present in collection you will get the following exception: "The given key was not present in the dictionary." To cope with that scenario use TryGetValue and determine what you want to do with items that do not have that key.
In addition instead of explicitly implementing the properties, as they all use the default implementation use auto-property:
public class MyItem
{
public int ID1 { get; set; }
public int ID2 { get; set; }
public int RegisteredTime { get; set; }
public SortedList<DateTime, AnotherItem> MySortedList { get; set; }
}

LINQ select all items of all subcollections that contain a string

I'm using jqueryui autocomplete to assist user in an item selection. I'm having trouble selecting the correct items from the objects' subcollections.
Object structure (simplified) is
public class TargetType
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<SubCategory> SubCategories { get; set; }
public TargetType()
{
SubCategories = new HashSet<SubCategory>();
}
}
public class SubCategory
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<SubTargetType> SubTargetTypes { get; set; }
public SubCategory()
{
SubTargetTypes = new HashSet<SubTargetType>();
}
}
Currently I'm doing this with nested foreach loops, but is there a better way?
Current code:
List<SubTargetResponse> result = new List<SubTargetResponse>();
foreach (SubCategory sc in myTargetType.SubCategories)
{
foreach (SubTargetType stt in sc.SubTargetTypes)
{
if (stt.Name.ToLower().Contains(type.ToLower()))
{
result.Add(new SubTargetResponse {
Id = stt.Id,
CategoryId = sc.Id,
Name = stt.Name });
}
}
}
You can do using Linq like this
var result = myTargetType.SubCategories
.SelectMany(sc => sc.SubTargetTypes)
.Where(stt => stt.Name.ToLower().Contains(type.ToLower()))
.Select(stt => new SubTargetResponse {
Id = stt.Id,
CategoryId = sc.Id,
Name = stt.Name });
The above query doesn't work. The following should work, but I'd not recommend that as that'd not be faster or more readable.
var result = myTargetType.SubCategories
.Select(sc => new Tuple<int, IEnumerable<SubTargetType>>
(sc.Id,
sc.SubTargetTypes.Where(stt => stt.Name.ToLower().Contains(type.ToLower()))))
.SelectMany(tpl => tpl.Item2.Select(stt => new SubTargetResponse {
Id = stt.Id,
CategoryId = tpl.Item1,
Name = stt.Name }));
Actually there are 2 different questions.
LINQ select all items of all subcollections that contain a string
Solutions:
(A) LINQ syntax:
var result =
(from sc in myTargetType.SubCategories
from stt in sc.SubTargetTypes.Where(t => t.Name.ToLower().Contains(type.ToLower()))
select new SubTargetResponse
{
Id = stt.Id,
CategoryId = sc.Id,
Name = stt.Name
})
.ToList();
(B) Method syntax:
var result =
myTargetType.SubCategories.SelectMany(
sc => sc.SubTargetTypes.Where(stt => stt.Name.ToLower().Contains(type.ToLower())),
(sc, stt) => new SubTargetResponse
{
Id = stt.Id,
CategoryId = sc.Id,
Name = stt.Name
})
.ToList();
Currently I'm doing this with nested foreach loops, but is there a better way?
Well, it depends of what do you mean by "better". Compare your code with LINQ solutions and answer the question. I personally do not see LINQ being better in this case (except no curly braces and different indentation, but a lot of a hidden garbage), and what to say about the second LINQ version in this answer - if that's "better" than your code, I don't know where are we going.

SelectListItem checkboxes and autopopulate with using Contains()

I have a list of courses. If a user is assigned to a course, then I want that checkbox to be checked. How come the Contains() is not accepted?
ViewModel:
public class ViewUserViewModel
{
public List<Cours> Courses { get; set; }
public List<UserCours> UserCoursesList { get; set; }
public AspNetUser user { get; set; }
public IEnumerable<SelectListItem> CourseList { get; set; }
}
Controller:
[HttpGet]
public ActionResult ViewUser(string id)
{
ViewUserViewModel model = new ViewUserViewModel();
model.user = db.AspNetUsers.FirstOrDefault(U => U.Id == id);
//List all courses
List<Cours> allCourses = db.Courses.OrderBy(c => c.CourseName).ToList();
model.Courses = allCourses;
//List of courses the user is assigned to
//var selectedCourse1 = db.UserCourses.Where(uc => uc.UserId == id).ToList();
model.UserCoursesList = db.UserCourses.Where(uc => uc.UserId == id).ToList();
//checkbox list
model.CourseList = allCourses.ToList().Select(x => new SelectListItem()
{
//Selected = selectedCourse1.Contains(x.CourseID),
Selected = model.UserCoursesList.Contains(x.CourseID),
Text = x.CourseName,
Value = x.CourseID.ToString()
});
}
I'm thinking the Selected property will test if List has the value assigned, then it would return back true. Instead, it's a syntax error and has invalid arguments. How can I compare the CourseList to the UserCoursesList?
I think you should use LINQ Any method. You not posted you UserCours class definition, but I guess that it should have an ID, and might look like that:
public class Cours
{
public int Id { get; set; }
// Other properties
}
In this case your check for Selected property will be:
Selected = model.UserCoursesList.Any(uc => uc.CourseID == x.CourseID)
Some tips:
In this case it will be better to use some meaningful name instead of
x, for example course. It will improve readability of the code.
allCourses is already a list, you do not need to call ToList()
method again.
Updated code: thanks to Aleksandr
[HttpGet]
public ActionResult ViewUser(string id)
{
ViewUserViewModel model = new ViewUserViewModel();
//Which user
model.user = db.AspNetUsers.FirstOrDefault(User => User.Id == id);
//List all courses
model.Courses = db.Courses.OrderBy(Courses => Courses.CourseName).ToList();
//List of courses the user is assigned to
model.UserCoursesList = db.UserCourses.Where(UserCourses => UserCourses.UserId == id).ToList();
//checkbox list
model.CourseList = model.Courses.Select(Course => new SelectListItem()
{
Selected = model.UserCoursesList.Any(UserCourse => UserCourse.CourseId == Course.CourseID),
Text = Course.CourseName,
Value = Course.CourseID.ToString()
});
return View(model);
}

c# build hierarchy in reverse

I have two methods to return a dynamic hierarchical structure from a flat List. The first works great using the recursive method here: (ID/ParentID) list to Hierarchical list.
I'm now trying to do the same thing except this time show only those categories and reports which have a saved report output. I'm not sure where to start as everything I find is building from root down and I need to go from the bottom up.
I get something like this now in my first method:
Category 1
|_Sub Category 1
|_Report 1
|_Report 2
|_Saved Output
Category 2
|_Sub Category 2
| |_Report 3
| |_Report 4
|_Sub Category 3
|_Report 5
|_Report 6
|_Saved Output
Category 3
|_Sub Category 4
|_Report 7
What I want in my second method is this:
Category 1
|_Sub Category 1
|_Report 2
|_Saved Output
Category 2
|_Sub Category 3
|_Report 6
|_Saved Output
Here's my basic test structure:
class Flat
{
public int id { get; set; }
public int parentId { get; set; }
public string name { get; set; }
public bool isOutput { get; set; }
public Flat(int i, int pid, string n, bool o)
{
this.id = i;
this.parentId = pid;
this.name = n;
this.isOutput = o;
}
}
class MyClass
{
public int id { get; set; }
public int parentId { get; set; }
public string name { get; set; }
public bool isOutput { get; set; }
public List<MyClass> children { get; set; }
public MyClass()
{
this.children = new List<MyClass>();
}
}
List<Flat> items = new List<Flat>()
{
new Flat(1,0,"Category 1",false),
new Flat(4,1,"Sub Category 1",false),
new Flat(8,4,"Report 1",false),
new Flat(9,4,"Report 2",false),
new Flat(15,9,"Saved Output",true),
new Flat(2,0,"Category 2",false),
new Flat(5,2,"Sub Category 2",false),
new Flat(10,5,"Report 3",false),
new Flat(11,5,"Report 4",false),
new Flat(6,2,"Sub Category 3",false),
new Flat(12,6,"Report 5",false),
new Flat(13,6,"Report 6",false),
new Flat(16,13,"Saved Output",true),
new Flat(3,0,"Category 3",false),
new Flat(7,3,"Sub Category 4",false),
new Flat(14,7,"Report 7",false)
};
To build from the bottom up, you need to start with all the leaf nodes that are valid (output == true), and then work upwards through all parent nodes until you reach the root. Here's one method that should work:
List<Flat> GetSavedOutput(List<Flat> items)
{
// get all output leaf nodes
var toAdd = items.Where (i => i.isOutput == true).ToList();
var result = new List<Flat>();
// grab all parent nodes that are not already included until
// there's nothing new to add
while (toAdd.Count > 0)
{
result.AddRange(toAdd);
toAdd = items.Where (i => !result.Contains(i)
&& result.Any (r => r.parentId == i.id)).ToList();
}
return result;
}
This is short and quick, and should work well for small, simple trees, but it is not the most efficient method because of processing the same nodes over and over again. A slightly more complex, but better method, would be to walk up the parent tree for each item:
List<Flat> GetSavedOutput(List<Flat> items)
{
var savedOutput = items.Where (i => i.isOutput == true).ToList();
var result = new List<Flat>();
foreach (var item in savedOutput) {
result.Add(item);
var temp = item;
do {
temp = items.Single (i => i.id == temp.parentId);
result.Add(temp);
} while (temp.parentId != 0);
}
return result;
}
If this is still not efficient enough, you can get a little more performance by storing references to the parent node in each Flat instance, so that the parent can be directly referenced in O(1) without having to look it up using a call to Single, which has efficiency O(n).
First I would suggest defining a recursive method to determine whether an item has a path to an output item, based on a given list:
static bool HasPathToOutput(List<Flat> items, Flat item)
{
if (item.isOutput)
{
return true;
}
// Recursively determine whether any of the item's children have
// a path to an output
return items.Where(i => i.parentId == item.id).Any(i => HasPathToOutput(items, i));
}
Then use that method to run your list through some LINQ queries, first getting the items that have a path to a saved output, then building the hierarchy, and finally, retrieving just the items that are at the top of their hierarchy:
// Generate a predicate based on the list
List<MyClass> foundItems =
items.Where(item => HasPathToOutput(items, item))
.Select(f => new MyClass { id = f.id, isOutput = f.isOutput, parentId = f.parentId, name = f.name })
.ToList();
// Generate child relationships
foundItems.ForEach(item => item.children = foundItems.Where(child => child.parentId == item.id).ToList());
// Filter out top-level items
List<MyClass> topLevel = foundItems.Where(i => i.parentId == 0).ToList();
You want to traverse your data depth-first order
This will look at all your leaf nodes first, add saved elements to the list and signal to the parent nodes that the root node was saved.
public bool StoreSavedElements(List<Tree> elements)
{
bool nodeSaved = false;
foreach (Tree child in childs)
{
if (child.StoreSavedElements(elements))
{
nodeSaved = true;
}
}
if (this.text == "Saved")
{
nodeSaved = true;
elements.Add(this);
}
return nodeSaved;
}

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