Select multiple types in LINQ Select - c#

Can this be turned into a select statement?
foreach (var gf in CreateGenericFieldsOnInspection(model))
{
simpleInspection.GenericFields.Add(gf.GenericFieldDefinition.Name,
new LC360Carrier.Domain.Models.Import.GenericField
{
GenericFieldType = GenericFieldValueType.Text,
Value = gf.Value
});
}

It looks like GenericFields is a Dictionary<string, GenericFieldOrSomething>. You could contort this into something really weird for the sake of using LINQ. But the purpose of LINQ is to query one or more IEnumerables to either get a result or transform them into something else.
It's just like SQL. You query it to either get a set of records or some value like the sum of some numbers.
In your case you've already got a result set - whatever CreateGenericFieldsOnInspection(model) returns. It makes sense to do what you're already doing - foreach through the results and perform some action on each one of them.
LINQ would be handy if you needed to query that set. For example,
var filteredProperties = CreateGenericFieldsOnInspection(model)
.Where(property => property.Name.StartsWith("X"));
But even then, once you had that collection, it would still make sense to use a foreach loop.
You'll see this sometimes - I did it when I first learned LINQ:
CreateGenericFieldsOnInspection(model).ToList()
.ForEach(property => DoSomethingWith(property));
We convert something to a List because then we can use .ForEach. But there's no benefit to it. It's just foreach with different syntax and an extra step.

I have an extension method that permits add. I was just having trouble w the syntax. Bagus Tesa, above was also helpful. Thanks.
simpleInspection.GenericFields = simpleInspection.GenericFields.Union(CreateGenericFieldsOnInspection(model).Select(x => new KeyValuePair<string, object>(x.GenericFieldDefinition.Name, new LC360Carrier.Domain.Models.Import.GenericField
{
GenericFieldType = GenericFieldValueType.Text,
Value = x.Value
}))).ToDictionary(x => x.Key, x => x.Value);

Related

Select 1 column from a Group By LINQ query

I think what I need is relatively simple but every example I Google just returns results using First(), which I'm already doing. Here is my expression:
var options = configData.AsEnumerable().GroupBy(row => row["myColumn"]).Select(grp => grp.First());
What I need is only ONE column from the grp portion and to be able to suffix .ToList() on there without an error. As it stands I receive 4 columns, but only need a specific one, kind of like if this (grp => grp["myColumn"]), didn't result in error the Error 153 Cannot apply indexing with [] to an expression of type 'System.Linq.IGrouping<object,System.Data.DataRow>'
Also, Key does not work in the grouping portion as these results are from a DataTable object. See here - >
If you want only the keys, you can use
var options = configData.AsEnumerable().Select(row=>row["myColumn"]).Distinct();
I think that this is what you want:
configData.AsEnumerable()
.GroupBy(r => r["myColumn"])
.Select(g => new
{
myColumnValue = g.Key,
myColumnItems = g.Select(r => r["OtherColumn"]).ToList()
});
Do you understand how/what this does though? Try it out and inspect the resulting IEnumerable. I'm not sure you have a perfect understanding on how GroupBy works but take your time with above example.
See this part:
new
{
myColumnValue = g.Key,
myColumnItems = g.Select(r => r["OtherColumn"]).ToList()
}
This creates an anonymous type which outputs the values of "OtherColumn" column into a list grouped by "myColumn" where value of "myColumn" is in the myColumnValue property.
I'm not sure this answers your question but it looks like this is what you want.
The variable g is of the type IGrouping<object, DataRow>, it's not DataRow. The IGrouping interface is designed to provide a list of DataRow's grouped by object values - it does not produce a flat list, if it did then it would just be a Sort, not GroupBy.
Just specify the field you want after your call to First() e.g.
.Select(grp => grp.FirstOrDefault()["MyFieldName"]);
This will take the first record from the grouping and select the specified field from that record.

Assigning values inside a LINQ Select?

I have the following query:
drivers.Select(d => { d.id = 0; d.updated = DateTime.Now; return d; }).ToList();
drivers is a List which comes in with different id's and updated values, so I am changing the values in the Select, but is the proper way to do it. I already know that I am not reassigning drivers to drivers because Resharper complains about it, so I guess it would be better if it was:
drivers = drivers.Select(d => { d.id = 0; d.updated = DateTime.Now; return d; }).ToList();
but is this still the way someone should assign new values to each element in the drivers List?
Although this looks innocent, especially in combination with a ToList call that executes the code immediately, I would definitely stay away from modifying anything as part of a query: the trick is so unusual that it would trip up readers of your program, even experienced ones, especially if they never saw this before.
There's nothing wrong with foreach loops - the fact that you can do it with LINQ does not mean that you should be doing it.
NEVER DO THIS. A query should be a query; it should be non-destructively asking questions of a data source. If you want to cause a side effect then use a foreach loop; that's what it's for. Use the right tool for the job.
Ok I will make an answer myself.
Xaisoft, Linq queries, be it lambda expression or query expression, shouldn't be used to mutate list. Hence your Select
drivers = drivers.Select(d => { d.id = 0; d.updated = DateTime.Now; return d; }).ToList();
is bad style. It confuses/unreadable, not standard, and against Linq philosophy. Another poor style of achieving the end result is:
drivers.Any(d => { d.id = 0; d.updated = DateTime.Now; return false; });
But that's not to say ForEach on List<T> is inappropriate. It finds uses in cases like yours, but do not mix mutation with Linq query, thats all. I prefer to write something like:
drivers.ForEach(d => d.updated = DateTime.Now);
Its elegant and understandable. Since it doesn't deal with Linq, its not confusing too. I don't like that syntax for multiple statements (as in your case) inside the lambda. It's a little less readable and harder to debug when things get complex. In your case I prefer a straight foreach loop.
foreach (var d in drivers)
{
d.id = 0;
d.updated = DateTime.Now;
}
Personally I like ForEach on IEnumerable<T> as a terminating call to Linq expression (ie, if the assignment is not meant to be a query but an execution).

How to force my lambda expressions to evaluate early? Fix lambda expression weirdness?

I have written the following C# code:
_locationsByRegion = new Dictionary<string, IEnumerable<string>>();
foreach (string regionId in regionIds)
{
IEnumerable<string> locationIds = Locations
.Where(location => location.regionId.ToUpper() == regionId.ToUpper())
.Select(location => location.LocationId); //If I cast to an array here, it works.
_locationsByRegion.Add(regionId, LocationIdsIds);
}
This code is meant to create a a dictionary with my "region ids" as keys and lists of "location ids" as values.
However, what actually happens is that I get a dictionary with the "region ids" as keys, but the value for each key is identical: it is the list of locations for the last region id in regionIds!
It looks like this is a product of how lambda expressions are evaluated. I can get the correct result by casting the list of location ids to an array, but this feels like a kludge.
What is a good practice for handling this situation?
You're using LINQ. You need to perform an eager operation to make it perform the .Select. ToList() is a good operator to do that. List is generic it can be assigned to IEnumberable directly.
In the case where you're using LINQ it does lazy evaluation by default. ToList/eager operations force the select to occur. Before you use one of these operators the action is not performed. It is like executing SQL in ADO.NET kind of. If you have the statement "Select * from users" that doesn't actually perform the query until you do extra stuff. The ToList makes the select execute.
Your closing over the variable, not the value.
Make a local copy of the variable so you capture the current value from the foreach loop instead:
_locationsByRegion = new Dictionary<string, IEnumerable<string>>();
foreach (string regionId in regionIds)
{
var regionToUpper = regionId.ToUpper();
IEnumerable<string> locationIds = Locations
.Where(location => location.regionId.ToUpper() == regionToUpper)
.Select(location => location.LocationId); //If I cast to an array here, it works.
_locationsByRegion.Add(regionId, LocationIdsIds);
}
Then read this:
http://msdn.microsoft.com/en-us/vcsharp/hh264182
edit - Forcing a eager evaluation would also work as others have suggested, but most of the time eager evaluations end up being much slower.
Call ToList() or ToArray() after the Select(...). Thus entire collection will be evaluated right there.
Actually the question is about lookup creation, which could be achieved simpler with standard LINQ group join:
var query = from regionId in regionIds
join location in Locations
on regionId.ToLower() equals location.regionId.ToLower() into g
select new { RegionID = regionId,
Locations = g.Select(location => location.LocationId) };
In this case all locations will be downloaded at once, and grouped in-memory. Also this query will not be executed until you try to access results, or until you convert it to dictionary:
var locationsByRegion = query.ToDictionary(x => x.RegionID, x => x.Locations);

LINQ - Using Select - understanding select

I find LINQ a little difficult to wrap my head around. I like the concept and believe it has lots of potential. But, after writing so much SQL the syntax is just not easy for me to swallow.
A. What is the deal with multiple ways to select?
I see that I am able to create a context and perform a Select() using a method.
context.Table.Select(lamba expression);
ok...Why would I use this? How does it compare to (or does it) this type of select?
var returnVal = from o in context.Table
orderby o.Column
select o;
B. Please explain the variable nature of
**from X** in context.Table
Why do we stick a seemingly arbitrarily named variable here? Shouldn't this be a known type of type <Table>?
So...
var returnVal = context.Table.Select(o => o);
and
var returnVal = from o in context.Table
select o;
are the same. In the second case, C# just has nice syntactic sugar to give you something closer to normal SQL syntax. Notice I removed the orderby from your second query. If you wanted that in there, then the first one would become:
var returnVal = context.Table.OrderBy(o => o.Column).Select(o => o);
As for your last question... we're not sticking an arbitrarily named variable here. We're giving a name to each row so that we can reference it later on in the statement. It is implicitly typed because the system knows what type Table contains.
In response to your comment, I wanted to add one more thought. You mentioned things getting nasty with the normal method calls. It really can. Here's a simple example where its immediately much cleaner (at least, if you're used to SQL syntax) in the LINQ syntax:
var returnVal = context.Table.OrderBy(o => o.Column1)
.ThenBy(o => o.Column2)
.ThenBy(o => o.Column3)
.Select(o => o);
versus
var returnVal = from o in context.Table
orderby o.Column1, o.Column2, o.Column3
select o;
A: this is the same. The compiler transforms the query expression to method calls. Exactly the same.
B: The x is the same as in foreach(var X in context.Table). You define a name for an individual element of the table/sequence.
In B, X's type is implicit. You could just as easily do something like:
from Row x in context.Table
and it would be the same. In A, there isn't any difference between using a lambda and the equivalent full-LINQ syntax, except that you would never do .Select(x => x). It's for transforming items. Say you had a list of integers, .Select(x => x * x) would return the square of each of them.

Replacing nested foreach with LINQ; modify and update a property deep within

Consider the requirement to change a data member on one or more properties of an object that is 5 or 6 levels deep.
There are sub-collections that need to be iterated through to get to the property that needs inspection & modification.
Here we're calling a method that cleans the street address of a Employee. Since we're changing data within the loops, the current implementation needs a for loop to prevent the exception:
Cannot assign to "someVariable" because it is a 'foreach iteration variable'
Here's the current algorithm (obfuscated) with nested foreach and a for.
foreach (var emp in company.internalData.Emps)
{
foreach (var addr in emp.privateData.Addresses)
{
int numberAddresses = addr.Items.Length;
for (int i = 0; i < numberAddresses; i++)
{
//transform this street address via a static method
if (addr.Items[i].Type =="StreetAddress")
addr.Items[i].Text = CleanStreetAddressLine(addr.Items[i].Text);
}
}
}
Question:
Can this algorithm be reimplemented using LINQ? The requirement is for the original collection to have its data changed by that static method call.
Update: I was thinking/leaning in the direction of a jQuery/selector type solution. I didn't specifically word this question in that way. I realize that I was over-reaching on that idea (no side-effects). Thanks to everyone! If there is such a way to perform a jQuery-like selector, please let's see it!
foreach(var item in company.internalData.Emps
.SelectMany(emp => emp.privateData.Addresses)
.SelectMany(addr => addr.Items)
.Where(addr => addr.Type == "StreetAddress"))
item.Text = CleanStreetAddressLine(item.Text);
var dirtyAddresses = company.internalData.Emps.SelectMany( x => x.privateData.Addresses )
.SelectMany(y => y.Items)
.Where( z => z.Type == "StreetAddress");
foreach(var addr in dirtyAddresses)
addr.Text = CleanStreetAddressLine(addr.Text);
LINQ is not intended to modify sets of objects. You wouldn't expect a SELECT sql statement to modify the values of the rows being selected, would you? It helps to remember what LINQ stands for - Language INtegrated Query. Modifying objects within a linq query is, IMHO, an anti-pattern.
Stan R.'s answer would be a better solution using a foreach loop, I think.
I don't like mixing "query comprehension" syntax and dotted-method-call syntax in the same statement.
I do like the idea of separating the query from the action. These are semantically distinct, so separating them in code often makes sense.
var addrItemQuery = from emp in company.internalData.Emps
from addr in emp.privateData.Addresses
from addrItem in addr.Items
where addrItem.Type == "StreetAddress"
select addrItem;
foreach (var addrItem in addrItemQuery)
{
addrItem.Text = CleanStreetAddressLine(addrItem.Text);
}
A few style notes about your code; these are personal, so I you may not agree:
In general, I avoid abbreviations (Emps, emp, addr)
Inconsistent names are more confusing (addr vs. Addresses): pick one and stick with it
The word "number" is ambigious. It can either be an identity ("Prisoner number 378 please step forward.") or a count ("the number of sheep in that field is 12."). Since we use both concepts in code a lot, it is valuable to get this clear. I use often use "index" for the first one and "count" for the second.
Having the type field be a string is a code smell. If you can make it an enum your code will probably be better off.
Dirty one-liner.
company.internalData.Emps.SelectMany(x => x.privateData.Addresses)
.SelectMany(x => x.Items)
.Where(x => x.Type == "StreetAddress")
.Select(x => { x.Text = CleanStreetAddressLine(x.Text); return x; });
LINQ does not provide the option of having side effects. however you could do:
company.internalData.Emps.SelectMany(emp => emp.Addresses).SelectMany(addr => Addr.Items).ToList().ForEach(/*either make an anonymous method or refactor your side effect code out to a method on its own*/);
You can do this, but you don't really want to. Several bloggers have talked about the functional nature of Linq, and if you look at all the MS supplied Linq methods, you will find that they don't produce side effects. They produce return values, but they don't change anything else. Search for the arguments over a Linq ForEach method, and you'll get a good explanation of this concept.
With that in mind, what you probaly want is something like this:
var addressItems = company.internalData.Emps.SelectMany(
emp => emp.privateData.Addresses.SelectMany(
addr => addr.Items
)
);
foreach (var item in addressItems)
{
...
}
However, if you do want to do exactly what you asked, then this is the direction you'll need to go:
var addressItems = company.internalData.Emps.SelectMany(
emp => emp.privateData.Addresses.SelectMany(
addr => addr.Items.Select(item =>
{
// Do the stuff
return item;
})
)
);
To update the LINQ result using FOREACH loop, I first create local ‘list’ variable and then perform the update using FOREACH Loop. The value are updated this way. Read more here:
How to update value of LINQ results using FOREACH loop
I cloned list and worked NET 4.7.2
List<TrendWords> ListCopy = new List<TrendWords>(sorted);
foreach (var words in stopWords)
{
foreach (var item in ListCopy.Where(w => w.word == words))
{
item.disabled = true;
}
}

Categories