How can the query below be modified to include a column for row number (ie: one-based index of results)?
var myResult = from currRow in someTable
where currRow.someCategory == someCategoryValue
orderby currRow.createdDate descending
select currRow;
EDIT1: I'm looking for the results to be {idx, col1, col2...col-n} not {idx, row}.
EDIT2: The row number should correspond to result rows not the table rows.
EDIT3: I DataBind these results to a GridView. My goal was to add a row number column to the GridView. Perhaps a different approach would be better.
Use the method-syntax where Enumerable.Select has an overload with the index:
var myResult = someTable.Select((r, i) => new { Row = r, Index = i })
.Where(x => x.Row.someCategory == someCategoryValue)
.OrderByDescending(x => x.Row.createdDate);
Note that this approach presumes that you want the original index of the row in the table and not in the filtered result since i select the index before i filter with Where.
EDIT: I'm looking for the results to be {idx, col1, col2...col-n} not
{idx, row}. The row number should correspond to result rows not
the table rows.
Then select the anonymous type with all columns you need:
var myResult = someTable.Where(r => r.someCategory == someCategoryValue)
.OrderByDescending(r => r.createdDate)
.Select((r, i) => new { idx = i, col1 = r.col1, col2 = r.col2, ...col-n = r.ColN });
Use this Select method:
Projects each element of a sequence into a new form by incorporating the element's index.
Example:
var myResult = someTable.Where(currRow => currRow.someCategory == someCategoryValue)
.OrderByDescending(currRow => currRow.createdDate)
.Select((currRow, index) => new {Row = currRow, Index = index + 1});
In response to your edit:
If you want a DataTable as result, you can go the non-Linq way by simply using a DataView and add a additional column afterwards.
someTable.DefaultView.RowFilter = String.Format("someCategory = '{0}'", someCategoryValue);
someTable.DefaultView.Sort = "createdDate";
var resultTable = someTable.DefaultView.ToTable();
resultTable.Columns.Add("Number", typeof(int));
int i = 0;
foreach (DataRow row in resultTable.Rows)
row["Number"] = ++i;
what about?
int i;
var myResult = from currRow in someTable
where currRow.someCategory == someCategoryValue
orderby currRow.createdDate descending
select new {Record = i++, currRow};
Just for fun, here's an alternative to Select with two arguments:
var resultsWithIndexes = myResult.Zip(Enumerable.Range(1, int.MaxValue - 1),
(o, i) => new { Index = i, Result = o });
According to you edit 1. NO, YOU CAN'T Linq returns the table as it is. You can build each column, but you lose the power of mapped entities.
This has been asked multiple times before: How do you add an index field to Linq results
There is no straightforward way if want to keep a flat list of columns (i.e. OP's Edit2) and also want a generic solution that works with any IEnumerable without requiring you to list out the set of expected columns.
However, there is a roundabout way to kinda go about it which is to dump the query results into a DataTable using the ToDataTable() method from here and then add a RowNumber column to that table.
var table = query.ToList().ToDataTable();
table.Columns.Add("RowNum", typeof(int));
int i = 0;
foreach (DataRow row in table.Rows)
row["RowNum"] = ++i;
This would likely cause performance issues with large datasets but it's not insanely slow either. On my machine a dataset with ~6500 rows took 33ms to process.
If your original query returned an anonymous type, then that type definition will get lost in the conversion so you'll lose the static typing on the column names of the resulting IEnumerable when you call table.AsEnumerable(). In other words, instead of being able to write something like table.AsEnumerable().First().RowNum you instead have to write table.AsEnumerable().First()["RowNum"]
However, if you don't care about performance and really want your static typing back, then you can use JSON.NET to convert the DataTable to a json string and then back to a list based on the anonymous type from the original query result. This method requires a placeholder RowNum field to be present in the original query results.
var query = (from currRow in someTable
where currRow.someCategory == someCategoryValue
orderby currRow.createdDate descending
select new { currRow.someCategory, currRow.createdDate, RowNum = -1 }).ToList();
var table = query.ToDataTable();
//Placeholder RowNum column has to already exist in query results
//So not adding a new column, but merely populating it
int i = 0;
foreach (DataRow row in table.Rows)
row["RowNum"] = ++i;
string json = JsonConvert.SerializeObject(table);
var staticallyTypedList = JsonConvert.DeserializeAnonymousType(json, query);
Console.WriteLine(staticallyTypedList.First().RowNum);
This added about 120ms to the processing time for my 6500 item dataset.
It's crazy, but it works.
I know I'm late to the party, but I wanted to show what worked for me.
I have a list of objects, and the object has an integer property on it for "row number"... or in this case, "Sequence Number". This is what I did to populate that field:
myListOfObjects = myListOfObjects.Select((o, i) => { o.SequenceNumber = i; return o; }).ToList();
I was surprised to see that this worked.
This one helped me in my case - Excel sheet extraction. anonymous type
var UploadItemList = ItemMaster.Worksheet().AsEnumerable().Select((x, index) => new
{
Code = x["Code"].Value == null ? "" : x["Code"].Value.ToString().Trim(),
Description = x["Description"].Value == null ? "" : x["Description"].Value.ToString().Trim(),
Unit = x["Unit"].Value == null ? "" : x["Unit"].Value.ToString().Trim(),
Quantity = x["Quantity"].Value == null ? "" : x["Quantity"].Value.ToString().Trim(),
Rate = x["Rate"].Value == null ? "" : x["Rate"].Value.ToString().Trim(),
Amount = x["Amount"].Value == null ? "" : x["Amount"].Value.ToString().Trim(),
RowNumber = index+1
}).ToList();
int Lc = 1;
var Lst = LstItemGrid.GroupBy(item => item.CategoryName)
.Select(group => new { CategoryName = group.Key, Items = group.ToList() ,RowIndex= Lc++ })
.ToList();
Related
I wanted to search for multiple values from the same column using OR in linq. In SQL, it is something like this:
var query = "Select * from table where id = 1";
query += "OR where id = 2";
The id is from an array of ids so i pass the id to a variable and loop it. The number of id in the array is not fixed and depends on how many ids chosen by the user by checking on the checkbox in the table. Because if i do as below, it will return null because it somewhat inteprets my where query as AND. How do i change it so that it will get rows from all ids(using OR)?
Request request = db.Requests;
var selectedIdList = new List<string>(arrId);
if (arrId.Length > 0)
{
for (var item = 0; item <= selectedIdList.Count() - 1; item++)
{
var detailId = Convert.ToInt32(selectedIdList[item]);
request = request.Where(y => y.Id == detailId);
}
}
Simply use || for OR and && for AND inside Where
But I prefer :
request.Where(y => selectedIdList.Contains(y.Id));
If selectedIdList is an array of Strings :
request.Where(y => selectedIdList.Contains(y.Id.ToString());
You can use LinqKit. Linqkit provides things to create predicate dynamically
Just install linqKit using packageManager, And use predicateBuilder to build predicate
var predicate = PredicateBuilder.True<Patient>();
predicate=predicate.And(...)
OR
predicate=predicate.Or(...)
And then use predicate in ur where clause
This way you can create Or (dynamically) as you would in dynamic SQL
You can get more details from here
https://www.c-sharpcorner.com/UploadFile/c42694/dynamic-query-in-linq-using-predicate-builder/
I used .Net framwork 4.0 with WinForm application component DataGridView and set DataSource with DataTable.Then there's a button to add row into DataGridView.
That code like this.
gridTable = (DataTable)dgrMainGrid.DataSource;
DataRow dr = gridTable.NewRow();
Before adding New Row into DataTable I checked if there's a duplicate row.To do that I used this LINQ Query.
//Item Code cannot duplicate
var results = from itmCode in gridTable.AsEnumerable()
where (itmCode.Field<string>("Item Code") == txtGrdItmLoc.Text)
select itmCode;
There after how I check the duplicate rows available or not in the data table?
if(//doWhatever function here ){
//if there's duplicate row values
isNotDuplicate = false;
}
else{
isNotDuplicate=true;
}
Before go to following step I need to get is there a duplicate or not and set it into isNotDuplicate variable or similar thing to check that. so i think to count the results rows but there's no such function to count 'var results`, Any possibility to do that?
if (!isDuplicate)
{
dr["#"] = true;
dr["Item Code"] = lSysItemCode;
dr["Stock Code"] = txtGdrItmItemLoc.Text;
dr["Brand"] = txtGrdItmBrand.Text;
dr["Model No"] = cmbGrdItmModel.SelectedValue.ToString();
gridTable.Rows.Add(dr);
dgrMainGrid.DataSource = gridTable;
}
I can use for loop with DataTable and check whether it's contain new value that equals to "Item Code" but I looking alternative method with linq.
Simply I'm looking replacement for this by using linq.
foreach (DataRow r in gridTable.Rows) {
if (r["Item Code"].ToString() == txtGrdItmLoc.Text) {
isDuplicate = true;
}
}
Sample Project : http://1drv.ms/1K4JnHt
Sample Code : http://pastebin.com/v7NMdUrf
You have not made it clear that in your DataTable if you are looking for duplicates for any specific Item Code or for any Item Code. Anyways,here is the code for both the scenarios:-
If you are looking for duplicates for any specific Item Code then you can simply check the count like this:-
bool istxtGrdItmLocDuplicate = gridTable.AsEnumerable()
.Count(x => x.Field<string>("ItemCode") == txtGrdItmLoc.Text) > 1;
If you are looking for duplicates in the entire DataTable, then simply group by Item Code and check the respective count for each Item Code like this:-
bool isDuplicate = gridTable.AsEnumerable()
.GroupBy(x => x.Field<string>("ItemCode")
.Any(x => x.Count() > 1);
IEnumerable<T> has a Count() method (check here), if you are not seeing it in intellisense then you are missing some using instruction, like using System.Linq; or some other...
Then you would just do:
if(results.Count()>0){
//if there's duplicate row values
isNotDuplicate = false;
}
else
{
isNotDuplicate=true;
}
First cast it to IEnumerable :
Convert DataRowCollection to IEnumerable<T>
Then you can use LINQ extension methods to do something like this (to check all values that has duplicates):
var duplicates = resultsList.Where(r => resultsList.Count(r2 => r2.Field<string>("Item Code") == r.Field<string>("Item Code")) > 0);
If you want check each value for duplicate you can use .Count method, something like this:
bool hasDuplicates = resultsList.Count(r2 => r2.Field<string>("Item Code") == "your code") > 1;
Ok, if for some reason this doesn't work you can write this function yourself:
public static class Helper
{
// or other collection type
public static int MyCount<T>(this IEnumerable<T> collection, Func<T, bool> function)
{
int count = 0;
foreach (T i in collection)
if (function(i)) ++count;
return count;
}
}
And use it like :
results.MyCount(r => r.Field<string>("Item Code") == "Item Code");
Based on this question:
[How can I do SELECT UNIQUE with LINQ?
I wrote the below expression to select rows with unique OrganizationID column from the dt datatabe which contains multiple columns.
var distinctRows = (from DataRow dRow in dt.Rows
select new { col1 = dRow["OrganizationID_int"] }).Distinct();
but when I check distinctRows after the expression being executed, it only has records with 1 column (col1) instead of holding the whole columns. I afraid that adding expressions like col2=... and etc, may be interpreted that I want select distinct on all these columns.
So how can I get the whole row while applying unique filter on only 1 column but not the whole columns?
I want the whole rows which satisfy that unique condition with all
columns. I want to iterate in the next step.
So you don't want to group by that field and return one of the multiple rows. You want only rows which are unique.
One way is using Enumerable.GroupBy and count the rows in each group:
var uniqueRows = dt.AsEnumerable()
.GroupBy(r => r.Field<int>("OrganizationID_int"))
.Where(g => g.Count() == 1)
.Select(g => g.First());
There is two versions of Distinct exception methods, one of them takes IEqualityComparar that can determine how you're going to distinguish different elements.
Here full example of how you can use this method:
class Item
{
public int Id {get; set;}
public string Name {get;set;}
}
class ItemComparer : IEqualityComparer<Item>
{
public bool Equals(Item x, Item y)
{
return x.Id == y.Id;
}
public int GetHashCode(Item x)
{
return x.Id;
}
}
void Main()
{
var sequence = new List<Item>()
{
new Item {Id = 1, Name = "1"},
new Item {Id = 1, Name = "2"}
};
// Using overloaded version of Distinct method!
var distinctSequence = sequence.Distinct(new ItemComparer());
// distinctSequence contains inly one Item with Id = 1
distinctSequence.Dump();
}
What you are looking for is GroupBy, followed by an aggregate function like Min, Sum, etc to select one of the row values for each column.
var distinctRows =
(from DataRow dRow in dt.Rows
group dRow by dRow["OrganizationID_int"] into g
select new { OrgId = g.Key; Col2 = g.First().Col2, Col3 = g.First().Col3 })
Use grouping with Linq to DataSet:
var distinctRows = from row in dt.AsEnumerable()
group row by new {
col1 = row.Field<int>("OrganizationID_int")
// other columns here
} into g
select g.First();
Have a look at MoreLinq's DistinctBy method, with which you can phrase your query like so:
dt.Rows.DistinctBy(dRow => dRow["OrganizationID_int"])
I am new to LINQ but am trying to tackle a tough one right off the bat. I am trying to do LINQ to dataset and emulate the following query...
SELECT smID, MIN(entID) FROM table
WHERE exID = :exID
AND smID IN (1,2,3,4,5,6,7,8, etc)
GROUP BY smID
The code I have so far is as follows...
DataTable dt = ds.Tables["myTable"];
var query =
from g in dt.AsEnumerable()
where g.Field<string>("exID") == exID
&& smIDs.Contains(g.Field<string>("smID"))
group g by g.Field<string>("smID") into rowGroup
select new
{
smID = rowGroup.Key,
minEntID = rowGroup.Min(g => g.Field<int>("entID"))
};
exID is a string variable in the method and smIDs is a List of strings also created earlier in the method. I created the following code to try and see my results and it throws an "System.InvalidCastException" error at query.Count...
if (query.Count() > 0)
{
foreach (var item in query)
{
string s = item.smID;
int i = (int)item.minEntID;
}
}
I have been unable to figure out what I am doing wrong.
VS points to...
minEntID = rowGroup.Min(g => g.Field<int>("entID"))
This is the first two lines of the stack trace...
at System.Data.DataRowExtensions.UnboxT`1.ValueField(Object value)
at System.Data.DataRowExtensions.Field[T](DataRow row, String columnName)
Any pointers would be most appreciated. Thanks.
Judging by the exception and stack trace, the type you're specifying for the endID field in your query doesn't match the DataType for that column in the DataTable. These must match -- you cannot use the Field method to cast the value to a different type.
I used Linqer to come up with this code:
from t in db.Table // your C# table / collection here, of course
where t.ExId == stackoverflow.ExId
&& (new int[] {1, 2, 3 }).Contains(t.SmId)
group t by new { t.SmId } into g
select new {
SmId = g.Key.SmId,
minEntID = g.Min(p => p.EntId)
}
I have a datatable which contains a load of dates. I wanted to group these by date and give each row a count.
I have managed to do this by dong the following:
IEnumerable query = from row in stats.AsEnumerable()
group row by row.Field<string>("date") into grp
select new { Date = grp.Key, Count = grp.Count(t => t["date"] != null) };
(where "stats" is the datatable)
I can see from debugging that this brings back the values all grouped as I need, but now I need to loop them and get each date and count.
My problem is I don't know how to retrieve the values!
I have a foreach loop
foreach (var rw in query)
{
string date = rw.Date; // <---- this is my problem?
}
I don't know what type my Ienumerable is to be able to reference the values in it!
So my question is how can I retrieve each date and count for each row by doing similar to the above?
I hope this makes sense!
This link on my blog should help you
http://www.matlus.com/linq-group-by-finding-duplicates/
Essentially your type is an anonymous type so you can't reference it as a type but you can access the properties like you're trying to do.
I think I see your issue. If you're trying to return it from a method, you should define a type and reuturn it like shown below:
public IEnumerable<MyType> GetQuery()
{
var query = from row in stats.AsEnumerable()
group row by row.Field<string>("date") into grp
select new { Date = grp.Key, Count = grp.Count(t => t["date"] != null) };
foreach (var rw in query)
{
yield return new MyType(rw.Date, rw.Count);
}
}
declare your "query" variable using "var" as shown above.
I guess you don't have access to the properties of the anonymous class because you're using IEnumerable query = .... Try var query = ... instead.
Going by your comment "I am returning the query from a function", which I take to mean that you want to do the query in a method, return the data to the caller, and then iterate the data in the caller, I suggest you return a Dictionary<DateTime, int>, like this:
static Dictionary<DateTime, int> GetSummarisedData()
{
var results = (
from row in stats.AsEnumerable()
group row by row.Field<string>("date") into grp
select new { Date = grp.Key, Count = grp.Count(t => t["date"] != null) })
.ToDictionary(val => val.Date, val => val.Count);
return results;
}
then in the caller you can just
foreach (var kvp in GetSummarisedData())
{
// Now kvp.Key is the date
// and kvp.Value is the count
}