How can I get an array of datatable row numbers which meet a certain criteria? For example I have a datatable with a column "DateTime". I want to retrieve the row numbers of the datatable where "DateTime" equals the variable startTime.
I know how to retrieve the actual row, but not the number of the row in the datatable.
Any help will be appreciated :)
int count = tblData.AsEnumerable()
.Count(row => row.Field<DateTime>("DateTime").Equals(startTime));
or as query:
int count = (from row in tblData.AsEnumerable()
where row.Field<DateTime>("DateTime").Equals(startTime)
select row).Count();
If I am reading the question right, using the overload of Select that allows a second input for the index may work for you. Something like
var indices =
table.AsEnumerable()
.Select((row, index) => new { row, index })
.Where(item => item.row.Field<DateTime?>("DateTime") == startTime)
.Select(item => item.index)
.ToArray();
If that date matches on the first, third, and sixth rows, the array will contain indices 0, 2, and 5. You can, of course, add 1 to each index in the query if you would like row numbers to start at 1. (ex: .Select(item => item.index + 1))
This is not possible. Note that with SQL (I assume you use SQL), the row order returned is not guaranteed. Your rows are ordered physically according to the primary key. So if you want a reliable row identifier, you must use your primary key number/id.
Related
I'm using the following SQL for calculating the value of a column named weight within a view.
I need to move this calculation logic to code.
CASE
WHEN SUM(BaseVal) OVER (PARTITION BY TEMPS.MandateCode) = 0 THEN 0
ELSE (BaseVal / (SUM(BaseVal) OVER (PARTITION BY TEMPS.MandateCode))) END AS [Weight]
Is iterating over each and grouping by MandateCode a good idea
var datatableenum = datatable.AsEnumerable();
foreach(var item in datatableenum)
{
List<DataTable> result = datatable.AsEnumerable()
.GroupBy(row => row.Field<int>("MandateCode"))
.Select(g => g.CopyToDataTable())
.ToList();
}
I'm going to say "no" because as you have it, it will perform the group operation for every mandate code, for each row then copy then to list, which adds up to a huge amount of burnt resources.. I would make a dictionary of mandatecode=>sum first and then use it when iterating the table
var d = datatable.AsEnumerable()
.GroupBy(
row => row.Field<int>("MandateCode"),
row => row.Field<double>("BaseVal")
).ToDictionary(g => g.Key, g => g.Sum());
Note I've no idea what type BaseVal is; you need to adjust this. If it's an integer remember that you'll be doing a calc of small_int/big_int eg 12/6152, which is always 0 so cast one of the operandi to eg double so the result will be like 0.1234
Then use the dictionary on each row
foreach(var item in datatableenum)
{
int sumbv = d[item.Field<int>("MandateCode"));
item["Weight"] = sumbv == 0 ? 0 : item.Field<double>("BaseVal") / sumbv;
}
I'm trying to select a distinct values from a DataTable using Linq. The DataTable gets populated from an excel sheet which has dynamic column apart from each excel sheet has a column name SERIAL NUMBER which is mandatory.
I have a DataTable for demo purpose which consist of 4 serial number as:
12345
12345
98765
98765
When I do
var distinctList = dt.AsEnumerable().Select(a => a).Distinct().ToList();
If I do
var distinctList = dt.AsEnumerable().Select(a => a.Field<string>("SERIAL NUMBER").Distinct().ToList();
Then I get the correct results, however but it only contains the one column from dt and not all the other columns
I get all four records instead of 2. Can someone tell me where I'm going wrong please.
The problem is that Distinct method by default uses the default equality comparer, which for DataRow is comparing by reference. To get the desired result, you can use the Distinct overload that allows you to pass IEqualityComparer<T>, and pass DataRowComparer.Default:
The DataRowComparer<TRow> class is used to compare the values of the DataRow objects and does not compare the object references.
var distinctList = dt.AsEnumerable().Distinct(DataRowComparer.Default).ToList();
For more info, see Comparing DataRows (LINQ to DataSet).
So, you want to group them by Serial Number and retrieve the full DataRow? Assuming that after grouping them we want to retrieve the first item:
var distinctList = dt.AsEnumerable().GroupBy(a => a.Field<string>("SERIAL NUMBER"))
.Select(a => a.FirstOrDefault()).Distinct().ToList();
EDIT: As requested
var distinctValues = dt.AsEnumerable().Select(a => a.Field<string>("SERIAL NUMBER")).Distinct().ToList();
var duplicateValues = dt.AsEnumerable().GroupBy(a => a.Field<string>("SERIAL NUMBER")).SelectMany(a => a.Skip(1)).Distinct().ToList();
var duplicatesRemoved = dt.AsEnumerable().Except(duplicateValues);
In ToTable method the first parameter specifies if you want Distinct records, the second specify by which column name we will make distinct.
DataTable returnVals = dt.DefaultView.ToTable(true, "ColumnNameOnWhichYouWantDistinctRecords");
Here there is no need to use linq for this task !
Using Linq a GroupBy would be better suited, by the sounds of it.
var groups = dt.AsEnumerable().GroupBy(a => a.SerialNumber).Select(_ => new {Key = _.Key, Items = _});
This will then contain groupings based on the Serial Number. With each group of items having the same serial number, but other property values different.
Try this:
List<string> distinctValues = (from row in dt.AsEnumerable() select row.Field<string>("SERIAL NUMBER")).Distinct().ToList();
However to me this also works:
List<string> distinctValues = dt.AsEnumerable().Select(row => row.Field<string>("SERIAL NUMBER")).Distinct().ToList();
I Have DataTable Similar Like this.
If the adults value and child value are same. I need to Remove it and count that. I need a output similar like this.
Can anyone please help me on this???.
Thank you,
You want to group by adults+child:
var groups = tblRoooms.AsEnumerable()
.GroupBy(r => new{ Adults = r.Field<int>("Adults"), Child = r.Field<int>("Child") });
var tblRooomsCopy = tblRoooms.Clone(); // creates an empty clone of the table
foreach(var grp in groups)
{
int roomCount = grp.Sum(r => r.Field<int>("Roomcount"));
DataRow row = tblRooomsCopy.Rows.Add();
row.SetField("RoomNo", grp.First().Field<int>("RoomNo"));
row.SetField("Roomcount", roomCount);
row.SetField("Adults", grp.Key.Adults);
row.SetField("Child", grp.Key.Child);
}
Now you have your desired result in tblRooomsCopy.
I won't write the complete code for you but I will describe a suggested way: first order the datatable by adults and child, that will cause same rows to be consecutive, create a list that you will fill rows to be deleted
then use foreach to compare each row with the previous one, if it has the same value then add it to the list of rows to be removed, finally you will delete the rows in the list
From the results of the SQL Server stored procedure 'sp_help' we get a DataSet containing a couple of tables. Seven if i am correct. One of them contains information about the constraints. Each foreign keys is documented by two consecutive rows in that table. The first row contains the constraint type, it's name and other details. It's followed by an empty row except for one column, 'constaint_keys' that contains the column names references of the constraint.
Other constraint types are described in one single row.
Any idea on how to "flatten" this information, using Linq, in a way that we can be sure that any couple made of two rows is really made of rows {n, n + 1}
Thank you for your help!
To gather elements of an IEnumerable by two :
data.Select((x, i) => new { Index = i, Value = x }).GroupBy(x => x.Index / 2)
You can use Enumerable.Range() to get the range of indexes you want to work with:
var unflattenedConstraints = constraintsTable.Rows;
var constraints = from index in Enumerable.Range(0, unflattenedConstraints.Count / 2)
.Select(x => x * 2)
let row1 = unflattenedConstraints[index]
let row2 = unflattenedConstraints[index + 1]
// Combine the rows
I'm doing a linq-to-sql query and I wish the LastOrDefault operator were available but it's not. So, I'm writing the query like this:
TheUserNote = ((from note in MyDC.UserNotes
where note.UserID == TheUserID
select note.NoteText).Skip(
(from n in MyDC.UserNotes
where n.UserID == TheUserID
select n.NoteID).Count() - 1)).SingleOrDefault(),
Basically, I want to use Skip and Count to get to the last item: count how many items there are, substract 1, and skip for that number.
It's not working and I'm looking to fix it. The problem is that sometimes Count can be 0 so I get an error saying parameters are not valid since in that case Count will be -1.
Any suggestions?
Thanks.
You might try .Reverse().FirstOrDefault(), or if you have a date column or primary key column try .OrderByDescending(...).FirstOrDefault().
Using your variable names and comment:
var TheUserNote
= MyDC
.UserNotes
.Where(x => x.UserId == TheUserID)
.OrderByDescending(x => x.NoteDateTime)
.FirstOrDefault()
;
Is there a specific order they are coming back in? I assume so otherwise why would it matter if it was the last or the first one you got?
Can't you just order by whatever in the opposite direction and use FirstOrDefault?