Linq Query to IEnumerable<T> Extension Method - c#

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);
}
}

Related

Linq to iterate through a collection and add another collection to its member

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.

LINQ select one field from list of DTO objects to array

I have DTO class that defines order line like this:
public class Line
{
public string Sku { get; set; }
public int Qty { get; set; }
}
A list of type Line is populated like so:
List<Line> myLines = new List<Line>();
myLines.Add(new Line() { Sku = "ABCD1", Qty = 1 });
myLines.Add(new Line() { Sku = "ABCD2", Qty = 1 });
myLines.Add(new Line() { Sku = "ABCD3", Qty = 1 });
What I want is to use LINQ to get an array of SKUs from the myLines List. How can I go about doing that?
I am currently doing it manually like this ...
// Get SKU List
List<string> mySKUs = new List<string>();
foreach (Line myLine in myLines)
mySKUs.Add(myLine.Sku);
string[] mySKUsArray = mySKUs.ToArray();
I was trying to google for a solution, but I wasn't sure how to word the question...
P.S. is there any benefit/performance gain in using LINQ method to achieve what I am currently doing with foreach?
You can use:
var mySKUs = myLines.Select(l => l.Sku).ToList();
The Select method, in this case, performs a mapping from IEnumerable<Line> to IEnumerable<string> (the SKU), then ToList() converts it to a List<string>.
Note that this requires using System.Linq; to be at the top of your .cs file.
This is very simple in LinQ... You can use the select statement to get an Enumerable of properties of the objects.
var mySkus = myLines.Select(x => x.Sku);
Or if you want it as an Array just do...
var mySkus = myLines.Select(x => x.Sku).ToArray();
I think you're looking for;
string[] skus = myLines.Select(x => x.Sku).ToArray();
However, if you're going to iterate over the sku's in subsequent code I recommend not using the ToArray() bit as it forces the queries execution prematurely and makes the applications performance worse. Instead you can just do;
var skus = myLines.Select(x => x.Sku); // produce IEnumerable<string>
foreach (string sku in skus) // forces execution of the query
You can select all Sku elements of your myLines list and then convert the result to an array.
string[] mySKUsArray = myLines.Select(x=>x.Sku).ToArray();
In the case you're interested in extremely minor, almost immeasurable performance increases, add a constructor to your Line class, giving you such:
public class Line
{
public Line(string sku, int qty)
{
this.Sku = sku;
this.Qty = qty;
}
public string Sku { get; set; }
public int Qty { get; set; }
}
Then create a specialized collection class based on List<Line> with one new method, Add:
public class LineList : List<Line>
{
public void Add(string sku, int qty)
{
this.Add(new Line(sku, qty));
}
}
Then the code which populates your list gets a bit less verbose by using a collection initializer:
LineList myLines = new LineList
{
{ "ABCD1", 1 },
{ "ABCD2", 1 },
{ "ABCD3", 1 }
};
And, of course, as the other answers state, it's trivial to extract the SKUs into a string array with LINQ:
string[] mySKUsArray = myLines.Select(myLine => myLine.Sku).ToArray();

Chain Lambda Linq

public class Translation
{
public string LanguageCode { get; set; }
public string Value { get; set; }
}
public class tblEnumJobFunction
{
public string strEnum { get; set; }
public List<Translation> mlgValue { get; set; } //mlgValue->MultiLingualValue
}
I have a List<tblEnumJobFunction> JobFunctionList with some data.
Example Data:
JobFunctionList[0].strEnum="ENUM_Manager";
JobFunctionList[0].mlgValue[0].LanguageCode ="EN";
JobFunctionList[0].mlgValue[0].Value="Manager";
JobFunctionList[0].mlgValue[1].LanguageCode ="DE";
JobFunctionList[0].mlgValue[1].Value="Geschäftsführer";
JobFunctionList[1].strEnum="ENUM_Student";
JobFunctionList[1].mlgValue[0].LanguageCode ="EN";
JobFunctionList[1].mlgValue[0].Value="Student";
JobFunctionList[1].mlgValue[1].LanguageCode ="DE";
JobFunctionList[1].mlgValue[1].Value="Schüler";
I can filter this list with LINQ by given country Code and happy with it.
The Question is how can I write equivalent below query syntax by lambda with the List/Collection extensions?
It is a cascade/chain query; looking into a list that is inside another list.
This query syntax is working OK.
string CountryCode ="EN";
var Query = from jobfunction in JobFunctionList
from translation in jobfunction.mlgValue
where translation.LanguageCode == CountryCode //'EN'
select translation;
The Result is;
List<string> JobList;
foreach (var translationitem in Query)
{
JobList.Add(translationitem .Value);
}
now I have
JobList[0]="Manager";
JobList[1]="Student";
For CountryCode="DE" I have;
JobList[0]="Geschäftsführer";
JobList[1]="Schüler";
Is there any way to write above query syntax with lambda similiar to this one?
JobFunctionList.Select(a=>a.mlgValue).Where(b=>b....)...
Two from clauses, as in your example, flatten your sequence. You need to use SelectMany extension method. This is probably what you are looking for:
List<string> JobList = Objs.SelectMany(jobFunction => jobFunction.mlgValue)
.Where(translation => translation.LanguageCode == CountryCode)
.Select(translation => translation.Value)
.ToList();
note: consider using good names, even for formal parameters with the small scope within lambdas . a, b, m, fo are not the best names for this.

Distinct by product id

Below is my class.
public class MyGroceryListItems
{
public int ProductId { get; set; }
public string ProductName { get; set; }
public int Quantity { get; set; }
}
Now I am fetching from database the values for this class and putting those classes in IList<MyGroceryListItems>. However, there are some duplicate items with same id but different quantity. How can I fetch only distinct entries by id?
I tried list.distinct but that didn't work because not all the entries are same in the record having same product id.
IEnumerable<MyGroceryListItems> items = ...;
var uniqueItemsByProdId =
items.GroupBy(x => x.ProductId).Select(g => g.First());
This will pick off a single (and somewhat arbitrary) item in the case that more than a item shares a ProductId with another.
Alternatively (and slightly faster), you could use a DistinctBy extension:
public static IEnumerable<T>
DistinctBy<T,TKey>(this IEnumerable<T> src, Func<T,TKey> selector)
{
HashSet<TKey> hs = new HashSet<TKey>();
foreach(var item in src)
{
//Add returns false if item is already in set
if(hs.Add(selector(item)))
{
yield return item;
}
}
}
like this:
items.DistinctBy(x => x.ProductId)
More useful, perhaps, is a query that gives the aggregate quantities for each item by ProductId:
items
.GroupBy(x => x.ProductId)
.Select(g => new MyGroceryListItems{
g.Key.ProductId,
g.Key.ProductName,
Quantity = g.Sum(gg => gg.Quantity)
})
You can implement an equality comparer. There is a solid example on msdn: http://msdn.microsoft.com/ru-ru/library/bb338049.aspx
This will give you more control over which items you consider equal. But involves more coding. If you want to select single and somewhat random item from all the items which share an id, then you'll be better off with spender's solution

How to sort on the Value of an member of a List<> within a parent List<>

I have a List sort question. I am using c# 3.0 and a generic List structure like this:
public class myObject
{
public int ID { get; set; }
public List<mySetting> setting { get; set; }
}
public class mySetting
{
public int ID { get; set; }
public string Name { get; set; }
public string Value { get; set; } // sort on this!
}
with this structure, I am filling a List of myObject with a LINQ query.
List<myObject> lmo = new List<myObject>();
lmo.SomeFillOperation():
What I want to do now is sort the entire List<myObject> on the individual <mySetting>[].Value values. EDIT: ( So this would be sorting on one keyed index of , for example mySetting[3].Value). I realize I could possibly do it in my SomeFillOperation(), but I want to do it after the List is formed.
Is there a recommended or easy way to do this? Is there a good example you have seen? Thanks in advance!
Well, List<T> already has a Sort method if you want to sort it in place - or you could use LINQ's OrderBy method. OrderBy is slightly easier than Sort:
var sorted = lmo.OrderBy(x => x.Value);
but even Sort isn't too bad:
lmo.Sort((x, y) => x.Value.CompareTo(y.Value));
EDIT: Having read the comment to the question, I no longer understand the question! Leaving this answer here as a potentially useful placeholder while I have dinner...
int MyObjectComparison(MyObject x, MyObject y)
{
return x.setting[0].Value.CompareTo(y.setting[0].Value);
}
lmo.Sort(MyObjectComparison);
Of course, this assumes that you want to use the Value of the first element in setting (and that setting is guarunteed to have at least one element). Solution with less assumption will be forthcoming when more info is given.
Since you are using 3.0, use LINQ:
var newList = lmo.OrderBy(i => i.Value);
and
var newList = lmo.OrderByDescending(i => i.Value);

Categories