This is my type:
public class myType
{
public int Id { get; set; }
public string name { get; set; }
}
And there is 2 collection of this type:
List<myType> FristList= //fill ;
List<myType> Excludelist= //fill;
And I need to exclude Excludelist from FristList something like the following:
List<myType> targetList =
FirstList.Where(m=>m.Id not in (Excludelist.Select(t=>t.Id));
What is your suggestion about the exact lambda expression of the above query?
Three options. One without any changes:
var excludeIds = new HashSet<int>(excludeList.Select(x => x.Id));
var targetList = firstList.Where(x => !excludeIds.Contains(x.Id)).ToList();
Alternatively, either override Equals and GetHashCode and use:
var targetList = firstList.Except(excludeList).ToList();
Or write an IEqualityComparer<MyType> which compares by IDs, and use:
var targetList = firstList.Except(excludeList, comparer).ToList();
The second and third options are definitely nicer IMO, particularly if you need to do this sort of work in various places.
Related
I have a situation where I need to iterate through a collection and add another collection to one of its member using Linq.
For example I have this class
public class Product
{
public string Car { get; set; }
public IEnumerable<Part> Part { get; set; }
}
This class would be within a collection like
IEnumerable<Product> ProductList
How can I populate the Part-property for each Product using GetPartData() with Linq
private IEnumerable<IEnumerable<Part>> GetPartData()
{
return new List<List<Part>>() {
new List<Part>{
new Part(){PartType="11",PartValue=1},
new Part(){PartType="12",PartValue=2}
},
new List<Part>{
new Part(){PartType="21",PartValue=1},
new Part(){PartType="22",PartValue=2}
}
};
}
So ultimately, my ProductList[0].Part should be equal to GetPartData()[0]
If both sequences should be linked via index you can use Enumerable.Zip:
ProductList = ProductList.Zip(GetPartData()
, (product, part) => new Product
{
Car = product.Car,
Part = part
})
.ToList();
Basically, you need to enumerate two IEnumerable at a time to match items from both. The ProductList and the result of GetPartData.
// The two IEnumerable
var products = ProductList;
var parts = GetPartData();
foreach((product, part) in (products, parts)) // will not work :(
{
product.Part = part;
}
Solutions has been debated before.
The Zip method will do it.
// The two IEnumerable
var products = ProductList;
var parts = GetPartData();
products.Zip(parts, (product, part) => product.Part = part).ToList();
The ToList() is really important, to force the execution.
If you are not comfortable with the lambda, you can do it like this:
// The two IEnumerable
var products = ProductList;
var parts = GetPartData();
products.Zip(parts, ProductPartAssociation).ToList();
...
Product ProductPartAssociation(Product product, IEnumerable<Part> part)
{
product.Part = part;
return product; // Actually not used.
}
The result of the Zip is an IEnumerable of whatever the ProductPartAssociation function return. You don't care about it, because what you need is just to be sure that the ProductPartAssociation is executed.
I was wondering if there's a simpler way to do this using LINQ or some other way...
I want to extract, from a List, all rows, grouping them by a particular field and storing the result in another different, like a List containing the grouped, not repeated, result of the search parameter.
Here's an example:
public class CustomObj
{
public int DOC { get; set; }
public string Desc { get; set; }
public string Name{ get; set; }
}
public class Worker()
{
public void DoWork()
{
List<CustomObj> objs = new List<CustomObj>();
objs.Add(new CustomObj(1, "Type1", "Name1"));
objs.Add(new CustomObj(1, "Type2", "Name1"));
objs.Add(new CustomObj(2, "Type2", "Name2"));
objs.Add(new CustomObj(3, "Type1", "Name1"));
objs.Add(new CustomObj(3, "Type2", "Name1"));
objs.Add(new CustomObj(3, "Type3", "Name1"));
// HERE'S WHAT I'D LIKE TO DO
// NOTE THAT THIS IS NOT A CORRECT FORMATED FUNCTION
List<int> docs = objs.GroupBy(o => o.DOC).Select(o => o.DOC).ToList<int>();
}
}
At the end my docs List should look like this
docs[0] = 1
docs[1] = 2
docs[2] = 3
In my current project I have, so far, 10 different objects, and I'm going to have to do this to all of them. Of course I'd like to avoid writing extensive functions to each one.
So if there's no other way at least I would like to know.
Thanks
What about:
List<int> docs = objs.Select(o => o.DOC).Distinct().ToList();
Or, if you want to use GroupBy:
var docs = objs.GroupBy(o => o.DOC).Select(o => o.Key).ToList();
I have such class:
public class SomeClass
{
public string Text1 { get; set; }
public string Text2 { get; set; }
public int Number { get; set; }
}
And I have list of this classes objects:
List<SomeClass> myList = new List<SomeClass>();
I want to query this list using LINQ (lambda syntax):
var result = myList.Where(obj => obj.Text1 == "SomeString");
Is there any way to pass property(eg. by string name), by which I want this LINQ query to be performed? In this example, I search by Text1 property, but let's say I want to invoke this search dynamically on Text1 or Text2 (determined in runtime). I want to be able to pass property name, on which this search is performed, and check whether this property is string, so that I'm sure this search CAN be performed first.
Is that possible? I know Reflections and Expressions have something to do about it, but I don't know them very well.
Thanks
The approach using Reflection:
var result = myList.Where(obj => obj.GetType()
.GetProperty("Text1")
.GetValue(obj)
.Equals("SomeString"));
With this way you can change from "Text1" to "Text2" property.
Another approach you can use dynamic linq:
var result = myList.AsQueryable().Where("Text1=#0", "SomeString");
Dynamic LINQ is also available via nuget.
You could use expression-trees?
string memberName = "Text1", value = "SomeString";
var p = Expression.Parameter(typeof(SomeClass), "obj");
var predicate = Expression.Lambda<Func<SomeClass, bool>>(
Expression.Equal(
Expression.PropertyOrField(p, memberName),
Expression.Constant(value,typeof(string))
), p);
var result = myList.AsQueryable().Where(predicate);
or alternative for the last line:
var result = myList.Where(predicate.Compile());
Consider this,
class Item
{
public string ID { get; set;}
public string Description { get; set; }
}
class SaleItem
{
public string ID { get; set;}
public string Discount { get; set; }
}
var itemsToRemoved = (List<Item>)ViewState["ItemsToRemove"];
// get only rows of ID
var query = from i in itemsToRemoved select i.ID;
var saleItems= (List<SaleItem>)ViewState["SaleItems"];
foreach (string s in query.ToArray())
{
saleItems.RemoveItem(s);
}
How can I write this LINQ phrase using IEnumerable/List Extension methods
// get only rows of ID
var query = from i in items select i.ID;
thanks in advance.
That one's easy:
var query = items.Select(i => i.ID);
A select clause always corresponds to a call to Select. Some of the other operators end up with a rather more complex expansion :) If you work hard, you can get the compiler to do some very odd stuff...
You can find all the details of this and other query expression translations in section 7.16 of the C# specification (v3 or v4).
<plug>
You could also buy C# in Depth, 2nd edition and read chapter 11 if you really wanted to :)</plug>
You can use this:
var query = items.Select(i => i.ID);
A couple of other points:
Here you don't need the call to ToArray:
foreach (string s in query.ToArray())
Also if your list is large and you are removing a lot of items you may want to use List.RemoveAll instead of iterating. Every time you remove an item from a list all the other items after it have to be moved to fill the gap. If you use RemoveAll this only has to be done once at the end, instead of once for every removed item.
List<Item> itemsToRemove = (List<Item>)ViewState["ItemsToRemove"];
HashSet<string> itemIds = new HashSet<string>(itemsToRemove.Select(s => s.ID));
saleItems.RemoveAll(c => itemIds.Contains(c.ID));
public static class ItemCollectionExtensions
{
public static IEnumerable<int> GetItemIds(this List<Item> list)
{
return list.Select(i => i.ID);
}
}
I have an example class containing two data points:
public enum Sort { First, Second, Third, Fourth }
public class MyClass
{
public MyClass(Sort sort, string name) {
this.Sort = sort;
this.Name = name;
}
public Sort Sort { get; set; }
public string Name { get; set; }
}
I'm looking to sort them into groups by their Sort property, and then alphabetize those groups.
List<MyClass> list = new List<MyClass>() {
new MyClass(MyClass.Sort.Third, "B"),
new MyClass(MyClass.Sort.First, "D"),
new MyClass(MyClass.Sort.First, "A"),
new MyClass(MyClass.Sort.Fourth, "C"),
new MyClass(MyClass.Sort.First, "AB"),
new MyClass(MyClass.Sort.Second, "Z"),
};
The output would then be:
(showing Name)
A
AB
D
Z
B
C
I've been able to do this by using a foreach to separate the items into many smaller arrays (grouped by the enum value) but this seems very tedious - and I think there must be some LINQ solution that I don't know about.
Using extension methods, first OrderBy the enum, ThenBy name.
var sorted = list.OrderBy( m => m.Sort ).ThenBy( m => m.Name );
Aside from the nice LINQ solutions, you can also do this with a compare method like you mentioned. Make MyClass implement the IComparable interface, with a CompareTo method like:
public int CompareTo(object obj)
{
MyClass other = (MyClass)obj;
int sort = this.srt.CompareTo(other.srt);
return (sort == 0) ? this.Name.CompareTo(other.Name) : sort;
}
The above method will order your objects first by the enum, and if the enum values are equal, it compares the name. Then, just call list.Sort() and it will output the correct order.
This should do it, I think
var result = from m in list
orderby m.Sort, m.Name
select m;