How to define local variable in a select LINQ-query - c#

I try to write this code:
returnModel.BunkerStat=tmpBunk.Select(b =>
new BunkerStatisticsRowModel
{
numberOfBunk=b.NumberOfBunk,
procentOfBunk=numberOfBunk/100
}).ToList();
But I get compile error because I cant reuse numberOfBunk to calculate procentOfBunk. Anybody know How I can reuse numberOfBunk? I dont want to repeat b.NumberOfBunk. Thanks.
SOLVED:
I tried like this and it worked:
returnModel.BunkerStat=tmpBunk.Select(b =>
new BunkerStatisticsRowModel
{
numberOfBunk=b.NumberOfBunk,
procentOfBunk=numberOfBunk/100
}).Select((p)=>
new BunkerStatisticsRowModel()
{
numberOfBunk=p.numberOfBunk,
procentOfBunk=p.numberOfBunk/100
}).ToList();

Use lambda with body
returnModel.BunkerStat = tmpBunk.Select(b => {
var procentOfBunk = b.NumberOfBunk / 100;
return new BunkerStatisticsRowModel
{
numberOfBunk = b.NumberOfBunk,
procentOfBunk = procentOfBunk
};
})
.ToList();

If you are able to add Properties in your Model BunkerStatisticsRowModel.
public class BunkerStatisticsRowModel
{
public int procentOfBunk { get { return numberOfBunk / 100; } }
public int numberOfBunk { get; set; }
}
Then use your linq statement like this.
returnModel.BunkerStat=tmpBunk.Select(b =>
new BunkerStatisticsRowModel
{
numberOfBunk=b.NumberOfBunk
}).ToList();
you should have your Collection which contains numberOfBunk and procentOfBunk.

You can use statement lambda to create a temporary variable.
returnModel.BunkerStat = tmpBunk
.Select(b =>
{
var procentOfBunk = b.NumberOfBunk / 100;
return new BunkerStatisticsRowModel
{
numberOfBunk = b.NumberOfBunk,
procentOfBunk = procentOfBunk,
};
})
.ToList();

Related

How to apply order by to linq query?

In c# linq, I have this code which works
private IQueryable<v_CompanyInquiryInfo> SearchCompany(string pCompanyName)
{
var mCompany = from d in db.v_CompanyInquiryInfo
where d.CompanyName.ToLower().Equals(pCompanyName)
select ((v_CompanyInquiryInfo)d);
return mCompany;
}
And I have this
private IQueryable SearchCompanies(string pValues)
{
string mValues = pValues;
foreach (string lWord in iRestrictedWords)
{
mValues.Replace(lWord, "");
}
var mSearchArray = pValues.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
var mCompanyCriteria = db.v_CompanyInquiryInfo.AsExpandable().Where(TradingInquiryController.CompanyContainsSearchTerms(mSearchArray));
var mCompanies = mCompanyCriteria
.Select(x => new
{
x.CompanyName,
x.CompanyID,
x.SearchTags,
Rank = mSearchArray.Sum(s => ((x.SearchTags.Length - x.SearchTags.Replace(s, "").Length) / s.Length))
});
var mResults = mCompanies.OrderByDescending(o => o.Rank).Skip(0).Take(20);
return mResults;
}
which also works. However I want the return type of this second function to be IQueryable<v_CompanyInquiryInfo>. The problem is there is a Rank new dynamic column added, and then I sort by it. How can I apply the same ordering but without creating a new column for it, then apply a cast to v_CompanyInquiryInfo. So that I can return IQueryable<v_CompanyInquiryInfo>. I can't figure out the syntax for this. Also it can return all columns.
Thanks
You don't need to create that property:
private IQueryable SearchCompanies(string pValues)
{
string mValues = pValues;
foreach (string lWord in iRestrictedWords)
{
mValues.Replace(lWord, "");
}
var mSearchArray = pValues.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
var mCompanyCriteria = db.v_CompanyInquiryInfo.AsExpandable().Where(TradingInquiryController.CompanyContainsSearchTerms(mSearchArray));
var mCompanies = mCompanyCriteria
.OrderByDescending(x => mSearchArray.Sum(s => ((x.SearchTags.Length - x.SearchTags.Replace(s, "").Length) / s.Length))
;
//var mResults = mCompanies.Skip(0).Take(20);
return mCompanies;
}

How do I conditionally update List<Object> items using LINQ

How do I conditionally update List<object> items using LINQ, I have managed to get a way to update all items in List<object> using this syntax:
var updatedList = sourceList.Select( x => { x.PropertyA = NewValue; return x; }).ToList();
var updatedList = sourceList.Select( x => {return x = x.PropertyA == SomeValue ? x1 : x;}).ToList();
You can either project it to a new collection using the .Select, or affect the original list using the .ForEach extension method. Both are shown below, given an arbitrary condition.
class Program
{
static void Main(string[] args)
{
List<testClass> ogList = new List<testClass>();
bool testCondition = true;
//use the select to update and project to new collection
var updated =
ogList
.Select(x =>
{
if (testCondition)
x.testProp = 1;
return x;
}).ToList();
//use the foreach to update original collection
ogList.ForEach(x =>
{
if (testCondition)
x.testProp = 1;
});
}
}
public class testClass
{
public int testProp { get; set; }
}
You can use this code:
sourceList.Where(w => w.CheckProperty == valueofProperty).Select(s => {
s.PropertyA = valueofPropertyA ;
s.PropertyB = valueofPropertyB;
return s;
}).ToList();
Hope this answer will help you.
So here I creating objects of my database, list of my db_table and table.
private DB dbs = new DB();
List<tab1> lst = new List<tab1>();
tab1 tabobj = new tab1();
here you will get the list of data in your table
lst = dbs.tab1.ToList();
````````````
updating the table
```````````
tabobj.c1 = "value";
tabobj.c2 = "value";
adding updated columns to table
dbs.tab1.Add(tabobj);
save changes here
dbs.SaveChanges();
data of updated table
lst = dbs.tab1.ToList();

Remove item from a list that is not in another list with LINQ

I have two lists which are different
public Ingrédient(int p_noIngrédient, string p_nomIngrédient, bool p_périssable,
double p_prixAuKilo)
{
NoIngrédient = p_noIngrédient;
NomIngrédient = p_nomIngrédient;
Périssable = p_périssable;
PrixAuKilo = p_prixAuKilo;
}
public Recette(int p_noPlat, int p_noIngrédient, double p_quantité)
{
NoPlat = p_noPlat;
NoIngrédient = p_noIngrédient;
Quantité = p_quantité;
}
I want to find all NoIngrédientin Ingrédient that are not in Recette. Right now I have this but it doesn't work.
void RetraitIngrédient(List<Recette> p_recettes,ref List<Ingrédient> p_ingrédients)
{
foreach (Recette recettes in p_recettes)
{
Ingrédient ingrédients =
p_ingrédients.Find(i => i.NoIngrédient != recettes.NoIngrédient);
WriteLine("{0,6} : {1:6}",ingrédients.NoIngrédient, ingrédients.NomIngrédient);
}
}
you could try something like this.
var results = _listIngredient.Join(_listRecette,
i => i.NoIngrédient,
r => r.NoIngrédient,
(ingre, rece) => new { ingre.NoIngrédient, ingre.NomIngrédient, ingre.PrixAuKilo, ingre.Périssable });

Merging lists with a for loop

I'm working on an algorithm which can generate 2 types of recommendations, restaurants and dishes. All of this works fine, but I wanted to merge these 2 types of recommendations in a single list, which is where I encountered some issues. From my previous question I concluded that I needed a wrapper class, which I have set up like this:
public class RecommenderItem
{
public Guid Id { get; set; }
public object Entity { get; set; }
}
Now I want to alternate the 2 types of recommendations so the list would look like this:
[Restaurant][Dish][Restaurant][Dish][Restaurant][Dish] //Etc...
Note that these recommendations are completely separate. They are generated purely based on the user's preference, and they have no correlation in between them. My product owner wants to show these recommendations on the home page of our app like this.
These lists are different in length, so if I have added all items from a list, I wanted to just add the remaining objects from the other list. A possible scenario of this could look like this:
/*Other objects before this...*/[Dish][Restaurant][Dish][Dish][Dish] //Etc...
Here did the list of restaurant objects run out and I just wanted to add the remaining dish recommendations at the end of the list.
I have gotten this far, but I'm unsure how I would catch an IndexOutOfBounds exception and add the rest of the remaining objects at the end.
public List<RecommenderItem> GetMergedRecommendationLists(List<Restaurant> restaurantRecommendations,
List<Dish> dishRecommendations)
{
//Setting up the output list.
List<RecommenderItem> output = new List<RecommenderItem>();
int count = 0;
//Check which list is longer and use that count
if (restaurantRecommendations.Count > dishRecommendations.Count)
count = dishRecommendations.Count;
else
count = restaurantRecommendations.Count;
for (int i = 0; i < count; i++)
{
//I'm fully aware this isn't the most optimal way of doing this,
//but I'm only looking at functionality here, optimizing performance comes later.
var restRecommendation = restaurantRecommendations[i];
var dishRecommendation = dishRecommendations[i];
output.Add(new RecommenderItem()
{
Id = restRecommendation.Id,
Entity = restRecommendation
});
output.Add(new RecommenderItem()
{
Id = dishRecommendation.Id,
Entity = dishRecommendation
});
}
return output;
}
Does anyone have an idea how I could do this? Could I just catch an IndexOutOfBounds exception and use .AddRange() for the remaining objects? I'm not sure how I could check which list was out of bounds.
Let me know if I should elaborate more and thanks in advance!
Edit: -removed because it wasn't fair.-
This is a fairly succinct way of doing this.
While not Linq, it works in the spirit of the way Linq works by deferring doing any work until the resulting sequence is enumerated:
public static IEnumerable<RecommenderItem> Merge(IEnumerable<Restaurant> restaurants, IEnumerable<Dish> dishes)
{
using (var r = restaurants.GetEnumerator())
using (var d = dishes.GetEnumerator())
{
while (true)
{
bool rAvailable = r.MoveNext();
bool dAvailable = d.MoveNext();
if (rAvailable)
yield return new RecommenderItem { Id = r.Current.Id, Entity = r.Current };
if (dAvailable)
yield return new RecommenderItem { Id = d.Current.Id, Entity = d.Current };
if (!rAvailable && !dAvailable)
break;
}
}
}
If you happen to be using the MoreLinq NuGet package that includes the ZipLongest extension method, you can use the following simplified implementation instead:
public static IEnumerable<RecommenderItem> Merge(IEnumerable<Restaurant> restaurants, IEnumerable<Dish> dishes)
{
foreach (var item in restaurants.ZipLongest(dishes, (r, d) => new { r, d }))
{
if (item.r != null)
yield return new RecommenderItem { Id = item.r.Id, Entity = item.r };
if (item.d != null)
yield return new RecommenderItem { Id = item.d.Id, Entity = item.d };
}
}
Addendum
As #InBetween posted in his answer, you can put the interleaving logic into an extension method. Here's my version; it's substantially the same, except I've added a small optimisation to avoid calling .MoveNext() when its not necessary:
public static class EnumerableExt
{
public static IEnumerable<T> Interleave<T>(this IEnumerable<T> a, IEnumerable<T> b)
{
using (var ae = a.GetEnumerator())
using (var be = b.GetEnumerator())
{
bool aAvailable = true;
bool bAvailable = true;
while (aAvailable || bAvailable)
{
aAvailable = aAvailable && ae.MoveNext();
bAvailable = bAvailable && be.MoveNext();
if (aAvailable)
yield return ae.Current;
if (bAvailable)
yield return be.Current;
}
}
}
}
Once you have that, I realised that you don't need to write an implict operator. Instead, you can just convert the two sequences to the resultant type before calling Interleave() like so:
var restaurantsAsRecommenderItems =
restaurantRecommendations
.Select(r => new RecommenderItem {Id = r.Id, Entity = r});
var dishesAsRecommenderItems =
dishRecommendations
.Select(d => new RecommenderItem {Id = d.Id, Entity = d});
var result =
restaurantsAsRecommenderItems
.Interleave(dishesAsRecommenderItems)
.ToList();
My recommendation would be to just make simple implicit operator :
public static implicit operator RecommenderItem(Restaurant restaurant) {
return new RecommenderItem { Id = restaurant.Id, Entity = restaurant };
}
Then you have possibility to convert these types easily like :
Restaurant rest = //...
RecommenderItem rItem = rest; // here the implicit operator is called
After doing this you can just use one for loop :
int count = Math.Max(restaurantRecommendations.Count, dishRecommendations.Count);
for ( int i = 0; i < count; i++ ) {
if ( i < restRecommendations.Count )
output.Add(restRecommendations[i]);
if ( i < dishRecommendations.Count )
output.Add(dishRecommendations[i]);
}
This will make your work much more easier.
Well, there are probably more elegant LINQ solutions but you have already most, it's also a very efficient approach:
public List<RecommenderItem> GetMergedRecommendationLists(List<Restaurant> restaurantRecommendations, List<Dish> dishRecommendations)
{
//Setting up the output list.
List<RecommenderItem> output = new List<RecommenderItem>();
int count = Math.Min(restaurantRecommendations.Count, dishRecommendations.Count);
for (int i = 0; i < count; i++)
{
var restRecommendation = restaurantRecommendations[i];
var dishRecommendation = dishRecommendations[i];
output.Add(new RecommenderItem()
{
Id = restRecommendation.Id,
Entity = restRecommendation
});
output.Add(new RecommenderItem()
{
Id = dishRecommendation.Id,
Entity = dishRecommendation
});
}
int remainingRestaurant = restaurantRecommendations.Count - count;
int remainingDishes = dishRecommendations.Count - count;
if (remainingRestaurant > 0)
{
for (int i = count; i < restaurantRecommendations.Count; i++)
{
var restRecommendation = restaurantRecommendations[i];
output.Add(new RecommenderItem()
{
Id = restRecommendation.Id,
Entity = restRecommendation
});
}
}
else if (remainingDishes > 0)
{
for (int i = count; i < dishRecommendations.Count; i++)
{
var dishRecommendation = dishRecommendations[i];
output.Add(new RecommenderItem()
{
Id = dishRecommendation.Id,
Entity = dishRecommendation
});
}
}
return output;
}
A simple way of doing it would be:
public static IEnumerable<T> Merge<T>(this IEnumerable<T> first, IEnumerable<T> second)
{
using (var firstEnumerator = first.GetEnumerator())
using (var secondEnumerator = second.GetEnumerator())
{
while (firstEnumerator.MoveNext())
{
yield return firstEnumerator.Current;
if (secondEnumerator.MoveNext())
{
yield return secondEnumerator.Current;
}
}
while (secondEnumerator.MoveNext())
{
yield return secondEnumerator.Current;
}
}
}
After having created two arrays of restaurants and dishes of the same type RecommenderItem, you can use the Zip method like :
var restaurants = restaurantRecommendations.Select(x => new RecommenderItem {
Id = x.Id,
Entity = x
}).ToArray();
var dishes = dishRecommendations.Select(x => new RecommenderItem {
Id = x.Id,
Entity = x
}).ToArray();
var output = restaurants.Zip(dishes, (r, d) => new[] { r, d })
.SelectMany(r => r).Concat(dishes.Skip(restaurants.Length))
.Concat(restaurants.Skip(dishes.Length));
Restaraunt and Dish would have to share a base type:
restaurantRecommendations.Select(item => new RecommenderItem()
{
Id = item.Id,
Entity = item
});
dishRecommendations.Select(item => new RecommenderItem()
{
Id = item.Id,
Entity = item
});
Once that's the case you could use something like this slightly modified version of Zip (from System.Linq):
private static IEnumerable<T> ZipThrough<T>(IEnumerable<T> first, IEnumerable<T> second)
{
if (first == null) throw new ArgumentNullException(nameof(first));
if (second == null) throw new ArgumentNullException(nameof(second));
using (var e1 = first.GetEnumerator())
{
using (var e2 = second.GetEnumerator())
{
while (true)
if (e1.MoveNext())
{
yield return e1.Current;
if (e2.MoveNext()) yield return e2.Current;
}
else if (e2.MoveNext())
{
yield return e2.Current;
}
else
{
break;
}
}
}
}

Entity can't pass short (int16) values out

This is really holding up the process.
I have a db of marks stored as short, and want to extract a single entry out and create summary statistics on it. It seems simple but I seem to have to jump through a ton of hoops to get it so must be missing something basic.
Here is the basic method, and this works happily. however all i can do with it is pass it to the DataGridView.
private void MarksSummary(string StudentID)
{
int ID = Convert.ToInt32(StudentID);
//get the average of the marks using entity
using (var context = new collegestudentsEntities1())
{
var StudentMarks = (from m in context.Marks
where m.StudIDFK == ID
select new
{
m.Marks1,
m.marks2,
m.Marks3,
m.Marks4
});
dataGridView1.DataSource = StudentMarks.ToList();
Anything else, seems to be ridiculously long winded.
Eg: I can't do this
var Marklist = new List<Int16>();
StudentMarks.ToList().ForEach(m => Marklist.Add(m));
as I get "cannot convert from 'AnonymousType#1' to 'short'"
or this
Marklist = StudentMarks.ToList();
or this
double av = Marklist.Average();
Yet I can do a forEach which is silly on one row of data
foreach (var s in StudentMarks)
{
Marklist.Add(s.Marks1);
Marklist.Add(s.marks2);
Marklist.Add(s.Marks3);
Marklist.Add(s.Marks4);
}
and this works outputting happily
txtMarksOverFifty.Text = Marklist.Count(s => s > 50).ToString();
txtMarksFailed.Text = Marklist.Count(s => s < 50).ToString();
So what am I missing to get the values out of the query easily?
Thanks for your help :-)
Your foreach is trying to add an anonymous type
select new
{
m.Marks1,
m.marks2,
m.Marks3,
m.Marks4
} //...
To a List<Int16> so it's not surprising that fails. What it looks like you want to do with that is:
StudentMarks.ToList().ForEach(m => Marklist.AddRange(new [] { m.Marks1, m.marks2, m.Marks3, m.Marks4 }));
Edit: If you're just looking for a solution with less code you might try:
using (var context = new collegestudentsEntities1())
{
var StudentMarks = (from m in context.Marks
where m.StudIDFK == ID
select new[]
{
m.Marks1,
m.marks2,
m.Marks3,
m.Marks4
}).SelectMany(mark => mark).ToList();
}
or simply:
List<Int16> Marklist = context.Marks.Where(mark => mark.StudIDFK == ID)
.SelectMany(m => new [] { m.Marks1, m.marks2, m.Marks3, m.Marks4 })
.ToList();
Look at what you are creating here:
select new
{
m.Marks1,
m.marks2,
m.Marks3,
m.Marks4
});
This is an object that contains shorts.
StudentMarks.ToList().ForEach(m => Marklist.Add(m));
Here you are trying to add an object to a list of shorts. Try:
StudentMarks.ToList().ForEach(m => {
Marklist.Add(m.Mark1);
Marklist.Add(m.Mark2);
Marklist.Add(m.Mark3);
Marklist.Add(m.Mark4);
}
);

Categories