C#: only one typecast in linq? - c#

I have a gridview and each row displays one instance of MyCustomType. The instance itself is stored in the Tag property of DataGridViewRow. Now I want to use linq to select certain rows, based on multiple criteria. This looks like this:
var rows = grid_series.Rows
.Cast<DataGridViewRow>()
.Where(x => ((MyCustomType)x).myProperty != string.Empty)
.Where(x => ((MyCustomType)x).myOtherProperty < 42);
But I really want to avoid to cast the Tag-object in every single where statement. Is there a way to cast the object only once and use repeatedly? I thought about using a select statement first. Then I do have to apply the cast only once, but then I'd have to "re-convert" each result back to DataGridViewRow which I don't think is possible.

What about selecting the Tag and do another Cast (or OfType<> of not all the rows contains MyCustomType) afterwards:
var rows = grid_series.Rows
.Cast<DataGridViewRow>().Select(r => r.Tag).Cast<MyCustomType>()
.Where(x => x.myProperty != string.Empty)
.Where(x => x.myOtherProperty < 42);
If you want IEnumerable<DataGridViewRow> you you can try:
var rows = grid_series.Rows
.Cast<DataGridViewRow>()
.Select(r => new { MyType = (MyCustomType)r.Tag, Row = r })
.Where(x => x.MyType.myProperty != string.Empty)
.Where(x => x.MyType.myOtherProperty < 42)
.Select(x => x.Row);

Related

Return a List of distinct values from DataGridView

As the topic says: Is there a way to return a list of distinct values from a certain Column of a DataGridView?
This should do what you asked for:
var vv = dataGridView1.Rows.Cast<DataGridViewRow>()
.Select(x => x.Cells[yourColumn].Value.ToString())
.Distinct()
.ToList();
Note that the simple version above assumes that there are only valid values. If you also may have new rows or empty cells you may want to expanded it like this:
var vv = dataGridView1.Rows.Cast<DataGridViewRow>()
.Where(x => !x.IsNewRow) // either..
.Where(x => x.Cells[column].Value != null) //..or or both
.Select(x => x.Cells[column].Value.ToString())
.Distinct()
.ToList();

Sort columns in datagridview

In this topic, I asked how to sort the rows by the sum of the values in the row. The solution was this:
var temp = myDataBaseDataSet.table1.Clone();
myDataBaseDataSet.table1.Rows.Cast<DataRow>()
.OrderByDescending(x => x.ItemArray.OfType<bool>().Count(b => b == true))
.ToList().ForEach(x =>
{
temp.Rows.Add(x.ItemArray);
});
this.table1DataGridView.DataSource = temp;
Now there's a new maybe stupid question: how to sort the column like rows in example above? Simply, to change the order of the columns (but save columns values)
I tried to make an application for an example. First I do the sorting in rows as already wrote. Then I need to swap the columns by the sum of the values of the columns and they should be placed in order of descending ( like on screenshot )
You can first get an ordered list of columns from DataTable based on count of checked items in columns. Then remove corresponding columns from grid and add them in order this way:
var columns = dt.Columns.Cast<DataColumn>()
.Where(x => x.DataType == typeof(bool))
.Select(x => new
{
Column = x,
Count = dt.Rows.Cast<DataRow>()
.Count(r => r.Field<bool>(x) == true)
}).OrderByDescending(x => x.Count)
.Select(x => x.Column).ToList();
columns.ForEach(x =>
{
var column = grid.Columns.Cast<DataGridViewColumn>()
.Where(c => c.DataPropertyName == x.ColumnName)
.FirstOrDefault();
if (column != null)
{
grid.Columns.Remove(column);
grid.Columns.Add((DataGridViewColumn)column.Clone());
column.Dispose();
}
});

Remove every first element of grouped collection

I have a collection of elements and some of these elements are duplicating. I need to extract all records but only the first record if the record is one of a duplicate set.
I was able to group the elements and find all elements that have duplicates, but how to remove every first element of a group?
var records =
dbContext.Competitors
.GroupBy(x => x.Email)
.Select(x => new { Properties = x,
Count = x.Key.Count() })
.Where(x => x.Count > 1)
.ToList();
EDIT: Seems like it's impossible to accomplish this task with EF, because it fails to translate the desired linq expression to SQL. I'll be happy if someone offer different approach.
To exclude the first record from each email-address group with more than one entry, you could do this:
var records = dbContext.Competitors
.GroupBy(x => x.Email)
.SelectMany(x => (x.Count() == 1) ? x : x.OrderBy(t=>t).Skip(1))
.ToList();
This is the logic :
Group by a property > Select every Group > (Possibly) Sort that > Skip first one
This can be turned into some linq code like this :
//use SelectMany to flat the array
var x = list.GroupBy(g => g.Key).Select(grp => grp.Skip(1)).SelectMany(i => i);

Linq select with filtering not working

I'm trying to select one field last record in filtered database (this is different than last inserted record). I tried with following code in controller but instead of field value, i'm getting "true" or "false", depending on if there's results after filtering or not.
List<Pozicije> poz = new List<Pozicije>();
poz = db.Pozicijes.Where(p => p.grupa == grupa)
.OrderBy(p => p.sifra_pozicije).ToList();
string pos = poz.Select(p => p.sifra_pozicije.Contains(s)).LastOrDefault().ToString();
can someone point me how to get value i need instead?
Try this instead. I've combined both parts of your query into one.
var pos =
Convert.ToString(db.Pozicijes.Where(p => p.grupa == grupa
&& p.sifra_pozicije.Contains(s))
.OrderByDescending(p => p.sifra_pozicije)
.Select(p => p.sifra_pozicije)
.FirstOrDefault());
If it doesn't work, you may need to tell us what types s and sifra_pozicije are.
LastOrDefault is not supported with LINQ to Entities/LINQ TO SQL. You need to do OrderByDescending and then get First record. Like:
string pos = db.Pozicijes.Where(p => p.grupa == grupa && p.sifra_pozicije.Contains(s)))
.OrderByDescending(p=> p.sifra_pozicije)
.Select(r=> r.sifra_pozicije)
.First();

Join an array of string with the result of an existing linq statement

As a follow up to my last question here:
Filtering a list of HtmlElements based on a list of partial ids
I need to take this statement:
doc.All.Cast<HtmlElement>()
.Where(x => x.Id != null)
.Where(x => ids
.Any(id => x.Id.Contains(id))).ToList();
and join it with an array of strings called fields. Assuming the array and list will have the same amount of elements each and line up correctly. I tried using Zip() but thought I might need to use an additional linq statement to make it work.
Assuming that fieldList[0] and IdList[0] corresponding to each other, you can do the following:
var IdList = doc.All.Cast<HtmlElement>()
.Where(x => x.Id != null)
.Where(x => ids
.Any(id => x.Id.Contains(id))).ToList();
var resultList = fieldList
.Select( (item, index) => new { Field = item, Id = IdList[index] })
.ToDictionary(x => x.Id, x => x.Field);
You have mentioned it already, you can use Enumerable.Join:
var joined = from id in fields
join ele in elements on id equals ele.Id
select new { Element = ele, ID = id };
var dict = joined.ToDictionary(x => x.ID, x => x.Element);
I've presumed that you want to join them via ID. I've also presumed that the string[] contains only unique ID's. Otherwise you need to use Distinct.

Categories