I have a list in c# :
public class Item
{
public int Id { get; set; }
public string Name { get; set; }
}
List<Item> items = new List<Item>()
{
new Item() { Id = 1, Name = "Item-1" },
new Item() { Id = 2, Name = "Item-2" },
new Item() { Id = 3, Name = "Item-3" },
new Item() { Id = 4, Name = "Item-4" },
new Item() { Id = 5, Name = "Item-5" },
};
Now i use where clause on the above list of items and fetch all items whose Id is greater than or equals to 3.
List<Item> itemsWithIdGreaterThan3 = items.Where(i => i.Id >= 3).ToList();
The above statement creates a new List but it copies the objects by reference, so if i change any object`s property in itemsWithIdGreaterThan3 list then it reflect the changes in item list:
itemsWithIdGreaterThan3[0].Name = "change-item-2"
This also changes the object with Id = 3 in items List.
Now what i want is to clone the object, so i found Select function like:
List<Item> itemsWithIdGreaterThan3 = items.Where(i => i.Id >= 3)
.Select(i => new Item() { Id = i.Id, Name = i.Name }).ToList();
This works, but what if i have an object contains 20 to 30 properties or even more. Then in than case we have to manually copy each property. Is there any shortcut solution for this problem ??
You could make a constructor for Item that takes an Item as it's parameter. Within that you would then do the property assignment. Then just call the constructor from the Select.
public class Item
{
public int Id { get; set; }
public string Name { get; set; }
public Item(Item i)
{
Id = i.Id;
Name = i.Name;
...
}
}
List<Item> itemsWithIdGreaterThan3 = items.Where(i => i.Id >= 3)
.Select(i => new Item(i)).ToList();
Related
Supposed that I have these classes
public class Subject
{
public int Id { get; set; }
public string Category { get; set; }
public string Type { get; set; }
}
public class Student
{
public int Id { get; set; }
public List<MySubject> MySubjects { get; set; }
}
public class MySubject
{
public int Id { get; set; }
public string Category { get; set; }
public string Type { get; set; }
public string Schedule { get; set; }
public string RoomNumber { get; set; }
}
sample data
var subjects = new List<Subject>()
{
new Subject(){ Id = 1, Category = "Mathematics", Type = "Algebra" },
new Subject(){ Id = 2, Category = "Computer Science", Type = "Pascal" }
};
var student = new Student()
{ Id = 1, MySubjects = new List<MySubject>() {
new MySubject() {Id = 1, Category = "Mathematics", Type = "Algebra" },
new MySubject() {Id = 3, Category = "Mathematics", Type = "Trigonometry"},
}
};
//TODO: Update list here
student.MySubjects.ForEach(i => Console.WriteLine("{0}-{1}-{2}\t", i.Id, i.Category, i.Type));
the above line of code returns
1-Mathematics-Algebra
3-Mathematics-Trigonometry
which is incorrect. I need to return this
1-Mathematics-Algebra
2-Computer Science-Pascal
Basically I would like to modify and iterate the student.MySubjects and check its contents against subjects.
I would like to remove the subjects (3-Mathematics-Trigonometry) that are not present in the subjects and also ADD subjects that are missing (2-Computer Science-Pascal).
Can you suggest an efficient way to do this by searching/comparing using Category + Type?
Try like below.
// Remove those subjects which are not present in subjects list
student.MySubjects.RemoveAll(x => !subjects.Any(y => y.Category == x.Category && y.Type == x.Type));
// Retrieve list of subjects which are not added in students.MySubjects
var mySubjectsToAdd = subjects.Where(x => !student.MySubjects.Any(y => y.Category == x.Category && y.Type == x.Type))
.Select(x => new MySubject() {
Id = x.Id,
Category = x.Category,
Type = x.Type
}).ToList();
// If mySubjectsToAdd has any value then add it into student.MySubjects
if (mySubjectsToAdd.Any())
{
student.MySubjects.AddRange(mySubjectsToAdd);
}
student.MySubjects.ForEach(i => Console.WriteLine("{0}-{1}-{2}\t", i.Id, i.Category, i.Type));
// make an inner join based on mutual values to filter out wrong subjects.
var filteredList =
from mySubject in student.MySubjects
join subject in subjects
on new { mySubject.Category, mySubject.Type }
equals new { subject.Category, subject.Type }
select new MySubject { Id = mySubject.Id, Category = mySubject.Category, Type = mySubject.Type };
// make a left outer join to find absent subjects.
var absentList =
from subject in subjects
join mySubject in filteredList
on new { subject.Category, subject.Type }
equals new { mySubject.Category, mySubject.Type } into sm
from s in sm.DefaultIfEmpty()
where s == null
select new MySubject { Id = subject.Id, Category = subject.Category, Type = subject.Type };
student.MySubjects = filteredList.ToList();
student.MySubjects.AddRange(absentList.ToList());
I have the following business objects:
public class ItemCategoryBO
{
public string ItemCategory { get; set; }
public string Title { get; set; }
}
public class ItemBO
{
public int ItemId { get; set; }
public string Title { get; set; }
public string ItemCategory { get; set; }
}
List<ItemCategoryBO> categoryList = new List<ItemCategoryBO>();
ItemCategoryBO itemCategory = new ItemCategoryBO();
itemCategory.ItemCategoryCd = "CARS";
itemCategory.Title = "Cars";
ItemCategoryBO itemCategory2 = new ItemCategoryBO();
itemCategory.ItemCategoryCd = "PLANES";
itemCategory.Title = "Planes";
categoryList.Add(itemCategory);
categoryList.Add(itemCategory2);
List<ItemBO> itemList = new List<ItemBO>();
ItemBO item1 = new ItemBO();
item1.ItemId = 1;
item1.Title = "1st item";
item1.ItemCategoryCd = "OTHER";
ItemBO item2 = new ItemBO();
item2.ItemId = 2;
item2.Title = "2nd Item";
item2.ItemCategoryCd = "CARS";
ItemBO item3 = new ItemBO();
item3.ItemId = 3;
item3.Title = "3rd Item";
item3.ItemCategoryCd = "PLANES";
itemList.Add(item1);
itemList.Add(item2);
itemList.Add(item3);
If I have a list of a few categories, how could I find a list of items that contain a category in the list of categories? (In my example, I want to get back items 2 and 3)
If you have a situation like:
List<ItemBO> items;
List<ItemCategoryBO> categories;
and you wish to get all the items that have a category that is in your list of categories, you can use this:
IEnumerable<ItemBO> result = items.Where(item =>
categories.Any(category => category.ItemCategory.equals(item.ItemCategory)));
The Any() operator enumerates the source sequence and returns true as soon as an item satisfies the test given by the predicate. In this case, it returns true if the categories list contains an ItemCategoryBO where its ItemCategory string is the same as the item's ItemCategory string.
More information about it on MSDN
Try using some linq
List<ItemBO> itm = new List<ItemBO>;
//Fill itm with data
//get selected item from control
string selectedcategory = cboCatetories.SelectedItem;
var itms = from BO in itm where itm.ItemCategory = selectedcategory select itm;
itms now contains all items in that category
Here's something I did in Linqpad
void Main()
{
var cat1 = new ItemCategoryBO {ItemCategory="c1", Title = "c1"};
var cat2 = new ItemCategoryBO {ItemCategory="c2", Title = "c2"};
var item1 = new ItemBO { ItemId = 1, Title = "item1", ItemCategory="c1"};
var item2 = new ItemBO { ItemId = 1, Title = "item2", ItemCategory="c2"};
var item3 = new ItemBO { ItemId = 1, Title = "item3", ItemCategory="c2"};
var item4 = new ItemBO { ItemId = 1, Title = "item4", ItemCategory="c3"};
var items = new List() {item1, item2, item3, item4};
var categories = new List() {cat1, cat2};
var itemsInCategory = from item in items
join category in categories on item.ItemCategory equals category.ItemCategory into itemInCategory
from categoryItem in itemInCategory
select new {item.Title, item.ItemCategory};
itemsInCategory.Dump();
}
// Define other methods and classes here
public class ItemCategoryBO
{
public string ItemCategory { get; set; }
public string Title { get; set; }
}
public class ItemBO
{
public int ItemId { get; set; }
public string Title { get; set; }
public string ItemCategory { get; set; }
}
This returns:
Title, ItemCategory
item1 c1
item2 c2
item3 c2
Try this:
List<ItemBO> items = ...;
ItemCategoryBO category = ...;
List<ItemBO> filteredItems = items
.Where( i => i.ItemCategory.Equals(category) )
.FirstOrDefault();
Updated to address OP's updated question:
If I have a list of a few categories, how could I find a list of items that contain a category in the list of categories? (In my example, I want to get back items 2 and 3)
I think you actually should do this in two steps. First, get your distinct list of items. Then, from your items, get your list of categories. So:
// First, get the distinct list of items
List<ItemBO> items = new List<ItemBO>();
foreach ( var category in categories )
{
foreach ( var item in category.Items )
{
if ( !items.Contains(item) )
items.Add(item);
}
}
// Second, get the list of items that have the category.
List<ItemBO> filteredItems = items
.Where( i => i.ItemCategory.Equals(category) )
.FirstOrDefault();
Hope this helps:
var result = (Object to search in).Where(m => (Object to compare to).Any(r => r.Equals(m.Key)).ToList();
I have a small hierarchy. Example:
entity:
public class MyClass
{
public int ID { get; set; }
public string Name { get; set; }
public int ParentID { get; set; }
}
My hierarchy data look like:
Id = 1 Name = Item1 ParentId = NULL
Id = 2 Name = Item2 ParentId = 1
Id = 3 Name = Item3 ParentId = 2
Id = 4 Name = Item4 ParentId = 2
Id = 5 Name = Item5 ParentId = 3
The problem is I need to sort it that child nodes must be after its immediate parent. The example bellow must look like
Id = 1 Name = Item1 ParentId = NULL
Id = 2 Name = Item2 ParentId = 1
Id = 3 Name = Item3 ParentId = 2
// the elements with parentID = 3
Id = 5 Name = Item5 ParentId = 3
//continue
Id = 4 Name = Item4 ParentId = 2
Any adwices?
Assuming you have a _list of MyClass objects, then sort it first on Name field, then on ParentId field, like shown below using LINQ:
_list.OrderBy(L=>L.Name).ThenBy(L=>L.ParentId);
Hope this may help.
Try this
I assume that 1st you want to order by parentid and in each parent you want to sort by id.
myClassList.OrderBy(parent=>parent.ParentId).ThenBy(parent=>parent.Id);
Try this recursive code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
MyClass.data = new List<MyClass>() {
new MyClass() { ID = 1, Name = "Item1", ParentID = null},
new MyClass() { ID = 2, Name = "Item2", ParentID = 1 },
new MyClass() { ID = 3, Name = "Item3", ParentID = 2 },
new MyClass() { ID = 4, Name = "Item4", ParentID = 2 },
new MyClass() { ID = 5, Name = "Item5", ParentID = 3 }
};
MyClass myClass = new MyClass();
myClass.GetData(null, 0);
Console.ReadLine();
}
}
public class MyClass
{
public static List<MyClass> data = null;
public int ID { get; set; }
public string Name { get; set; }
public int? ParentID { get; set; }
public void GetData(int? id, int level)
{
List<MyClass> children = data.Where(x => x.ParentID == id).ToList();
foreach (MyClass child in children)
{
Console.WriteLine(" {0} ID : {1}, Name : {2}, Parent ID : {3}", new string(' ',4 * level),child.ID, child.Name, child.ParentID);
GetData(child.ID, level + 1);
}
}
}
}
Here you have a way to do it. As you can see, I overrode the ToString method and added a few more cases.
public class MyClass
{
public int Id { get; set; }
public string Name { get; set; }
public int? ParentId { get; set; }
public override string ToString()
{
return string.Format("{0}: {1} - {2}", Id, Name, ParentId);
}
}
class Program
{
static void Main(string[] args)
{
List<MyClass> list = new List<MyClass>();
list.Add(new MyClass { Id = 1, Name = "Item1", ParentId = null });
list.Add(new MyClass { Id = 2, Name = "Item2", ParentId = 1 });
list.Add(new MyClass { Id = 3, Name = "Item3", ParentId = 2 });
list.Add(new MyClass { Id = 4, Name = "Item4", ParentId = 2 });
list.Add(new MyClass { Id = 5, Name = "Item5", ParentId = 3 });
list.Add(new MyClass { Id = 6, Name = "Item6", ParentId = 1 });
list.Add(new MyClass { Id = 7, Name = "Item7", ParentId = null });
list.Add(new MyClass { Id = 8, Name = "Item8", ParentId = 2 });
list.Add(new MyClass { Id = 9, Name = "Item9", ParentId = 6 });
list.Add(new MyClass { Id = 10, Name = "Item10", ParentId = 7 });
foreach(var item in list.Where(x => !x.ParentId.HasValue).OrderBy(x => x.Id))
ProcessItem(item, list, 0);
Console.ReadKey();
}
private static void ProcessItem(MyClass item, List<MyClass> list, int level)
{
Console.WriteLine("{0}{1}", new string(' ', level * 2), item.ToString());
foreach (var subitem in list.Where(x => x.ParentId == item.Id).OrderBy(x => x.Id))
ProcessItem(subitem, list, level + 1);
}
}
Would something like this work for you?
If you need an actual ordered list, try this:
foreach (var item in OrderList(list))
Console.WriteLine(item.ToString());
(...)
private static List<MyClass> OrderList(List<MyClass> list)
{
List<MyClass> orderedList = new List<MyClass>(list.Count());
foreach (var item in list.Where(x => !x.ParentId.HasValue).OrderBy(x => x.Id))
AddItem(item, list, orderedList);
return orderedList;
}
private static void AddItem(MyClass item, List<MyClass> list, List<MyClass> orderedList)
{
orderedList.Add(item);
foreach (var subitem in list.Where(x => x.ParentId == item.Id).OrderBy(x => x.Id))
AddItem(subitem, list, orderedList);
}
The following should do the trick (and show some better performance because we save the hierarchy in a lookup, instead of searching the IEnumerable on the fly):
public List<MyClass> SortHierarchically(IEnumerable<MyClass> myClasses)
{
if(myClasses == null)
return new List<MyClass>();
var myClassesByParentId = myClasses.ToLookup(mc => mc.ParentId);
var result = new List<MyClass>(myClasses.Count());
int? currentParentId = null;
MyClass currentItem = myClassesByParentId[currentParentId].Single();
result.Add(currentItem);
currentParentId = currentItem.Id;
if(myClassesByParentId.Contains(currentParentId))
result.AddRange(myClassesByParentId[currentParentId].SelectMany(mc => GetAllSortedChildren(mc, myClassesByParentId)));
return result;
}
public List<MyClass> GetAllSortedChildren(MyClass parent, ILookup<int?, MyClass> myClassesByParentId)
{
var result = new List<MyClass>() { parent };
if(myClassesByParentId.Contains(parent.Id))
retsult.AddRange(myClassesByParentId[parent.Id].SelectMany(mc => GetAllSortedChildren(mc, myClassesByParentId)));
return result;
}
It would be interesting to find a method of sorting this by standard LINQ, with some clever comparer or such.
One of the answers above works well. This is a generic version.
public static class SortingMethods
{
public static IList<T> OrderByHierarchical<T>(
this IEnumerable<T> items,
Func<T, string> getId,
Func<T, string> getParentId)
{
if (items == null)
return new List<T>();
var itemsByParentId = items.ToLookup(item => getParentId(item));
var result = new List<T>(items.Count());
var currentParentId = "";
var currentItem = itemsByParentId[currentParentId].Single();
result.Add(currentItem);
currentParentId = getId(currentItem);
if (itemsByParentId.Contains(currentParentId))
result.AddRange(itemsByParentId[currentParentId].SelectMany(item => GetAllSortedChildren(item, itemsByParentId, getId)));
return result;
}
private static IList<T> GetAllSortedChildren<T>(T parent, ILookup<string, T> itemsByParentId, Func<T, string> getId)
{
var result = new List<T>() { parent };
if (itemsByParentId.Contains(getId(parent)))
{
result.AddRange(itemsByParentId[getId(parent)].SelectMany(item => GetAllSortedChildren(item, itemsByParentId, getId)));
}
return result;
}
}
I'm having trouble conceptualizing something that should be fairly simple using LINQ. I have a collection that I want to narrow down, or filter, based on the id values of child objects.
My primary collection consists of a List of Spots. This is what a spot looks like:
public class Spot
{
public virtual int? ID { get; set; }
public virtual string Name { get; set; }
public virtual string Description { get; set; }
public virtual string TheGood { get; set; }
public virtual string TheBad { get; set; }
public virtual IEnumerable<Season> Seasons { get; set; }
public virtual IEnumerable<PhotographyType> PhotographyTypes { get; set; }
}
I'm trying to filter the list of Spots by PhotographyType and Season. I have a list of ids for PhotographyTypes and Seasons, each in an int[] array. Those lists look like this:
criteria.PhotographyTypeIds //an int[]
criteria.SeasonIds //an int[]
I want to build a collection that only contains Spots with child objects (ids) matching those in the above lists. The goal of this functionality is filtering a set of photography spots by type and season and only displaying those that match. Any suggestions would be greatly appreciated.
Thanks everyone for the suggestions. I ended up solving the problem. It's not the best way I'm sure but it's working now. Because this is a search filter, there are a lot of conditions.
private List<Spot> FilterSpots(List<Spot> spots, SearchCriteriaModel criteria)
{
if (criteria.PhotographyTypeIds != null || criteria.SeasonIds != null)
{
List<Spot> filteredSpots = new List<Spot>();
if (criteria.PhotographyTypeIds != null)
{
foreach (int id in criteria.PhotographyTypeIds)
{
var matchingSpots = spots.Where(x => x.PhotographyTypes.Any(p => p.ID == id));
filteredSpots.AddRange(matchingSpots.ToList());
}
}
if (criteria.SeasonIds != null)
{
foreach (int id in criteria.SeasonIds)
{
if (filteredSpots.Count() > 0)
{
filteredSpots = filteredSpots.Where(x => x.Seasons.Any(p => p.ID == id)).ToList();
}
else
{
var matchingSpots = spots.Where(x => x.Seasons.Any(p => p.ID == id));
filteredSpots.AddRange(matchingSpots.ToList());
}
}
}
return filteredSpots;
}
else
{
return spots;
}
}
You have an array of IDs that has a Contains extension method that will return true when the ID is in the list. Combined with LINQ Where you'll get:
List<Spot> spots; // List of spots
int[] seasonIDs; // List of season IDs
var seasonSpots = from s in spots
where s.ID != null
where seasonIDs.Contains((int)s.ID)
select s;
You can then convert the returned IEnumerable<Spot> into a list if you want:
var seasonSpotsList = seasonSpots.ToList();
This may helps you:
List<Spot> spots = new List<Spot>();
Spot s1 = new Spot();
s1.Seasons = new List<Season>()
{ new Season() { ID = 1 },
new Season() { ID = 2 },
new Season() { ID = 3 }
};
s1.PhotographyTypes = new List<PhotographyType>()
{ new PhotographyType() { ID = 1 },
new PhotographyType() { ID = 2 }
};
Spot s2 = new Spot();
s2.Seasons = new List<Season>()
{ new Season() { ID = 3 },
new Season() { ID = 4 },
new Season() { ID = 5 }
};
s2.PhotographyTypes = new List<PhotographyType>()
{ new PhotographyType() { ID = 2 },
new PhotographyType() { ID = 3 }
};
List<int> PhotographyTypeIds = new List<int>() { 1, 2};
List<int> SeasonIds = new List<int>() { 1, 2, 3, 4 };
spots.Add(s1);
spots.Add(s2);
Then:
var result = spots
.Where(input => input.Seasons.All
(i => SeasonIds.Contains(i.ID))
&& input.PhotographyTypes.All
(j => PhotographyTypeIds.Contains(j.ID))
).ToList();
// it will return 1 value
Assuming:
public class Season
{
public int ID { get; set; }
//some codes
}
public class PhotographyType
{
public int ID { get; set; }
//some codes
}
I have a database return result which has flatten results like below. I want to use Linq to break the flat results into primary classes with the items populating the primary class items property collection.
public class Result
{
public string PrimaryKey { get; set; }
public string Status { get; set; }
public string ItemName { get; set; }
}
public class ObjectA
{
public string PrimaryKey { get; set; }
public string Status { get; set; }
public List<Item> Items = new List<Item>();
}
public class Item
{
public string Name { get; set; }
}
static void Main(string[] args)
{
GetObjectAs();
}
static List<ObjectA> GetObjectAs()
{
// this is our table results
List<Result> results = new List<Result>();
results.Add(new Result()
{
PrimaryKey = "1",
Status = "Done",
ItemName = "item1"
});
results.Add(new Result()
{
PrimaryKey = "2",
Status = "Fail",
ItemName = null
});
results.Add(new Result()
{
PrimaryKey = "3",
Status = "Done",
ItemName = "item2"
});
results.Add(new Result()
{
PrimaryKey = "3",
Status = "Done",
ItemName = "item3"
});
List<ObjectA> returnResults = new List<ObjectA>();
// need to break into 3 ObjectA objects
// ObjectA 1 needs an Item added to its Items collection with ItemName item1
// ObjectA 2 has no items since the ItemName above is null
// ObjectA 3 needs 2 Items added to its Items collection item2 and item3
// return our collection
return returnResults;
}
PS this is just sample code, I know you shouldn't expose a List as a public property and should return an IEnumerator instead of the actual List etc.
You can use GroupBy to group the results by the primary key, then you can operate on the subset of rows within the group to obtain the status (hopefully all values for Status are the same, which is why I used First) and the list of items.
var items = results.GroupBy(r => r.PrimaryKey).Select(grp => new ObjectA()
{
PrimaryKey = grp.Key,
Status = grp.Select(r => r.Status).First(),
Items = grp.Where(r => r.ItemName != null)
.Select(r => new Item() { Name = r.ItemName }).ToList()
}).ToList();
return results
.GroupBy(r => r.PrimaryKey)
.Select(grp => new ObjectA
{
PrimaryKey = grp.Key,
Status = grp.First().Status,
Items = grp.Where(i => i.ItemName != null).Select(i => new Item { Name = i.ItemName }).ToList()
}).ToList();