How to convert foreach loop with a switch statement into LINQ - linq

I have following:
foreach (var x in items)
{
switch (x.name)
{
case firstname1:
case firstname2:
case firstname3: .
x.Value = somemethodcall;
break;
}
}
switch has about 15 different values to check against. The x has 3 properties: name, value and enum constant to check x.name against. items is a collection of x objects with many enum constants, but I only want to find certain ones and apply same method to alter the x.value property.
Is it possible to use linq to convert my foreach as one liner?

Try this
items.ToList().Where(x => x.name == firstname1 || x.name == firstname2).ForEach(x => x.Value = somemethodcall)

Your code is long because of switch instead of foreach.
switch (x.name)
{
case firstname1:
case firstname2:
case firstname3:
can be written as
var names = new []{firstname1, firstname2, firstname3};
if (names.Contains(x.name))
foreach itself can be written as
items.Select(SetValueByName);
where
string[] names = new []{firstname1, firstname2, firstname3};
TypeOfX SetValueByName(TypeOfX x)
{
if(names.Contains(x.name))
{
x.Value = somemethodcall;
}
return x;
}
if the optimization doesn't apply to your scenario, just
TypeOfX SetValueByName(TypeOfX x)
{
.. original switch statement
return x;
}

Related

Better way to check resultset from LINQ projection than List.Count?

Is there a better way to check if a LINQ projection query returns results:
IList<T> TList = db.Ts.Where(x => x.TId == 1).ToList(); // More canonical way for this?
if (TitleList.Count > 0)
{
// Result returned non-zero list!
string s = TList.Name;
}
You can use Any(), or perhaps more appropriately to your example, SingleOrDefault(). Note that if you are expecting more than one result and plan to use all of them, then it doesn't really save anything to use Any() instead of converting to a List and checking the length. If you don't plan to use all the results or you're building a larger query that might change how the query is performed then it can be a reasonable alternative.
var item = db.Ts.SingleOrDefault( x => x.TId == 1 );
if (item != null)
{
string s = item.Name;
...
}
or
var query = db.Ts.Where( x => x.Prop == "foo" );
if (query.Any())
{
var moreComplexQuery = query.Join( db.Xs, t => t.TId, x => x.TId );
...
}

Linq query: MAX in WHERE

I have a long query that returns
Item {
DateTime entryDate
.....
}
I like to combine the result of this query with another table
Value {
DateTime date,
double value
}
such that if entryDate >= CUTOFF, then take the value on CUTOFF, else take the value on entryDate. In other words, I'd want to achieve:
SELECT Item.*, Value.value WHERE
MIN( Item.entryDate, CUTOFF ) == Value.date
Excuse my syntax, but that's the idea.
EDIT: After some trial and error, I came up with this linq-to-sql query:
from iValue in Values
join iItem in ... (long query)
let targetDate = iItem.EntryDate > CUTOFF ? iItem.EntryDate : CUTOFF
where iValue.Date == targetDate
select new
{
iItem,
targetDate,
iValue
}
Thanks for your help.
yourLongQuery.Where(y => y.Item.entryDate == Value.date || CUTOFF == Value.date)
.Select(x => new {
entrydate = (x.Item.entryDate < CUTOFF ? x.Item.entryDate : CUTOFF),
/*rest of x.Item properties here */ ,
x.Value.date,
x.Value.value
});
Filter the query, Combine the two items into one item and modify the first item
Given that you've returned your data from your Item query and that the Value table is relatively small then this is a nice way to go:
var lookup = values.ToLookup(v => v.date, v => v.value);
var query =
from i in items
let c = i.entryDate < CUTOFF ? i.entryDate : CUTOFF
let v = lookup[c].FirstOrDefault()
select new
{
Item = i,
Value = v,
};
The ToLookup extension is very useful and often overlooked.
such that if entryDate >= CUTOFF, then take the value on CUTOFF, else
take the value on entryDate. In other words, I'd want to achieve:
SELECT Item.*, Value.value WHERE MAX( Item.entryDate, CUTOFF ) ==
Value.date
This is contradictory - if entryDate >= CUTOFF then take the value on CUTOFF imples that you want MIN(Item.entryDate, CUTOFF), not MAX.
Having said that, you just want to select the value.Value that matches each item of your query. Each item should look up the relevant value which matches your MAX (or, I believe, MIN) statement.
query.Select(item =>
{
var matchingValue = Values.Single(v =>
v.date == Min(item.entryDate, CUTOFF));
return new { item, matchingValue.value };
});
This will return an IQueryable of anonymous { Item, double } objects.
If you require this to be a executed as a single SQL statement you'll need to do some refactoring. A good start is to swap matchingValue into a single statement.
query.Select(item => new { item, context.Values.Single(v =>
v.date == Min(item.entryDate, CUTOFF)).value });
I don't have a Visual Studio in front of me to confirm, but I am not sure that the Math.Min function is mapped in LINQ-to-SQL. Let's assume it's not.
query.Select(item => new { item, context.Values.Single(v =>
v.date == (item.entryDate < CUTOFF ? item.entryDate : CUTOFF)).value });
I believe that will resolve to a single query if you execute it with a .ToList() but can't confirm until I have some tools in front of me. Test it with SQL profiler to be sure.

How to convert a foreach within a foreach to LINQ, if it's possible

I have a foreach that is within a foreach and I would love to convert it to LINQ. If it was just 1 foreach I could use a Where but I am confused. Here is my code.
Basically I need to find the invoice but it depends on metadata which is 2 levels down. I think the code is self-explanatory.
currentMetaData >>> Is created outside of the foreach code
It's an object, but the code below works.
foreach (var item in this.invoices.Items)
{
foreach (var metaData in item.MetaDatas)
{
if (metaData == currentMetaData)
{
name = item.Name;
break;
}
}
}
I would love to shorten it with LINQ. Is this possible?
This would give all the names
var name =
from i in this.invoices
from m in i.Metadata
where m == currentMetadata
select i.Name;
Only First Value to simulate the break; statement in your existing loop.
string name =
(from i in this.invoices
from m in i.Metadata
where m == currentMetadata
select i.Name).First();

How to join results from two different sets in LINQ?

I get some data about customers in my database with this method:
public List<KlientViewModel> GetListOfKlientViewModel()
{
List<KlientViewModel> list = _klientRepository.List().Select(k => new KlientViewModel
{
Id = k.Id,
Imie = k.Imie,
Nazwisko = k.Nazwisko,
Nazwa = k.Nazwa,
SposobPlatnosci = k.SposobPlatnosci,
}).ToList();
return list;
}
but also I have another method which counts value for extra field in KlientViewModel - field called 'Naleznosci'.
I have another method which counts value for this field based on customers ids, it looks like this:
public Dictionary<int, decimal> GetNaleznosc(List<int> klientIds)
{
return klientIds.ToDictionary(klientId => klientId, klientId => (from z in _zdarzenieRepository.List()
from c in z.Klient.Cennik
where z.TypZdarzenia == (int) TypyZdarzen.Sprzedaz && z.IdTowar == c.IdTowar && z.Sprzedaz.Data >= c.Od && (z.Sprzedaz.Data < c.Do || c.Do == null) && z.Klient.Id == klientId
select z.Ilosc*(z.Kwota > 0 ? z.Kwota : c.Cena)).Sum() ?? 0);
}
So what I want to do is to join data from method GetNaleznosc with data generated in method GetListOfKlientViewModel. I call GetNaleznosc like this:
GetNaleznosc(list.Select(k => k.Id).ToList())
but don't know what to do next.
Having obtained the dictionary:
var dict = GetNaleznosc(list.Select(k => k.Id).ToList());
You can now efficiently look up the decimal value of Naleznosci for a given client:
foreach (var k in list)
k.Naleznosci = dict[k.Id];
Now you have merged the values into the main list. Is this what you mean?
By the way, in your function that builds the dictionary, you make it accept a List<int>, but then all you do is call ToDictionary on it, which only requires IEnumerable<int>. So change the parameter type to that, and then you can call it:
var dict = GetNaleznosc(list.Select(k => k.Id));
This removes the call to ToList, which avoids making an unnecessary intermediate copy of the whole list of Ids. Probably won't make much difference in this case if you're hitting a database and then building up a large set of results in memory, but maybe worth bearing in mind for other uses of these operations.
Also, looking again at the helper function, there is no apparent advantage in building up the set of results in a dictionary for a list of ids, because each one is handled independently. You could simply put:
public decimal GetNaleznosc(int klientId)
{
return (from z in _zdarzenieRepository.List()
from c in z.Klient.Cennik
where z.TypZdarzenia == (int) TypyZdarzen.Sprzedaz && z.IdTowar == c.IdTowar && z.Sprzedaz.Data >= c.Od && (z.Sprzedaz.Data < c.Do || c.Do == null) && z.Klient.Id == klientId
select z.Ilosc*(z.Kwota > 0 ? z.Kwota : c.Cena)).Sum() ?? 0);
}
That is, provide a function that discovers just one value. Now you can directly build the right list:
public List<KlientViewModel> GetListOfKlientViewModel()
{
return _klientRepository.List().AsEnumerable().Select(k => new KlientViewModel
{
Id = k.Id,
Imie = k.Imie,
Nazwisko = k.Nazwisko,
Nazwa = k.Nazwa,
SposobPlatnosci = k.SposobPlatnosci,
Naleznosci = GetNaleznosc(k.Id)
}).ToList();
}

conditional assignments in Linq to SQL methods

How do I do something like the following, assinging new values based on condition within a linq method but keeping all results.
int x= 100;
var results= from r in Results where r.id==id select r
{ if r.value==x set r.result="Found"}
You're not really meant to - ideally queries shouldn't have side effects. I mean you can do:
var results = Results.Where(r => r.id == id)
.Select(r => {
if (r.value == x)
{
r.result = "Found";
}
return r;
};
I'd generally try not to though...
Note that this really won't work in LINQ to SQL etc.
It's probably better to make a second pass so you can keep your query clean and side-effect-free.
int x = 100;
List<Result> results = (from r in Results
where r.id == id
select r).ToList();
results.ForEach(r => r.result = r.value == x ? "Found" : "Not Found");
Linq should not be used in that way. I would iterate over the results of the query and then change them.
var results= from r in Results where r.id==id select r;
foreach (var r in results.ToList()) { if (r.value==x) r.result="Found"; }

Resources