I have got this situation with a datatable like this
C1 C2 C3
A AA 4
BB 6
B CC 3
DD 3
EE 4
C FF 5
GG 5
and my output should be like this
C1 C2 C3
A AA,BB 10
B CC,DD,EE 10
C FF,GG 10
How can i group by the column with the space till the next value comes up
What i did was i took all the row itemarray and then using some string manipulation and regex got the row value as for the first two values like this and assigned to a variable in a query using Let
A,AA,BB,10|B,CC,DD,EE,10 but then i cannot add it using the
**DT.clone.rows.Add(x.split("|"c))* method as there its not incrementing and adding the whole joined string
Any other input where i can manipulate and add it (P.S i know linq is querying language)
Thank you for your time
You can use .GroupBy to get result needed
Here is your class:
public class Data
{
public string C1 { get; set; }
public string C2 { get; set; }
public int C3 { get; set; }
}
Imagine that you have list of Data objects, so your GroupBy expression will be following:
var result = list.GroupBy(g => g.C1, (a, b) => new {C1 = a, C2 = b.ToList()})
.Select(g => new
{
g.C1,
C2 = string.Join(",", g.C2.Select(m => m.C2)),
C3 = g.C2.Sum(m => m.C3)
})
.ToList();
A simple .GroupBy can give you expected result, Edited to handle Null or WhiteSpace Columns
var res = ListModel.Where(e => !string.IsNullOrWhiteSpace(e.C1)
&& !string.IsNullOrWhiteSpace(e.C2))
.GroupBy(e => e.C1).Select(e => new
{
e.Key,
c2 = string.Join(",", e.Select(x => x.C2).ToList()),
c3 = e.Sum(x => x.C3)
}).ToList();
Hello All first of all Thank you for your time and effort i Did this use case using this code
This gave me all row item array in string and than in the end with a little Split method i was able to add it to my datatable
String.Join("|",(System.Text.RegularExpressions.Regex.Replace(String.Join("|",(From roww In DT.AsEnumerable() Select String.Join(",",roww.ItemArray) ).ToList),"\|,",",")).Split("|"c).
Select(Function(q)CStr(q)+","+CStr(String.join("|",System.Text.RegularExpressions.Regex.Matches(CStr(q),"\d+").Cast(Of match)).Split("|"c).Sum(Function(r) CInt(r) ))).tolist),",\d+,",",")```
Try following code which is tested
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
DataTable dt1 = new DataTable();
dt1.Columns.Add("C1", typeof(string));
dt1.Columns.Add("C2", typeof(string));
dt1.Columns.Add("C3", typeof(int));
dt1.Rows.Add(new object[] { "A", "AA", 4});
dt1.Rows.Add(new object[] { null, "BB", 6});
dt1.Rows.Add(new object[] { "B", "CC", 3});
dt1.Rows.Add(new object[] { null, "DD", 3});
dt1.Rows.Add(new object[] { null, "EE", 4});
dt1.Rows.Add(new object[] { "C", "FF", 5});
dt1.Rows.Add(new object[] { null, "GG", 5});
//replace nulls in column 1 with actual values
string previous = "";
foreach(DataRow row in dt1.AsEnumerable())
{
if (row.Field<string>("C1") == null)
{
row["C1"] = previous;
}
else
{
previous = row.Field<string>("C1");
}
}
DataTable dt2 = dt1.Clone();
var groups = dt1.AsEnumerable().GroupBy(x => x.Field<string>("C1")).ToList();
foreach (var group in groups)
{
dt2.Rows.Add(new object[] {
group.Key,
string.Join(",", group.Select(x => x.Field<string>("C2"))),
group.Select(x => x.Field<int>("C3")).Sum()
});
}
}
}
}
Yet another way using Skip, TakeWhile, and GroupBy extensions:
DataTable dt1 = new DataTable();
dt1.Columns.Add("C1", typeof(string));
dt1.Columns.Add("C2", typeof(string));
dt1.Columns.Add("C3", typeof(int));
//The output table.
DataTable dt2 = dt1.Clone();
dt1.Rows.Add(new object[] { "A", "AA", 3 });
dt1.Rows.Add(new object[] { null, "BB", 6 });
dt1.Rows.Add(new object[] { "B", "CC", 3 });
dt1.Rows.Add(new object[] { null, "DD", 3 });
dt1.Rows.Add(new object[] { null, "EE", 4 });
dt1.Rows.Add(new object[] { "C", "FF", 5 });
dt1.Rows.Add(new object[] { null, "GG", 6 });
var rows = dt1.Rows.Cast<DataRow>().AsEnumerable();
foreach (var row in rows.Where(r => r.Field<string>("C1") != null))
{
var indx = dt1.Rows.IndexOf(row) + 1;
var q = rows
.Skip(indx)
.TakeWhile(t => t.Field<string>("C1") == null)
.GroupBy(g => g.Field<string>("C1"))
.Select(g => new
{
C1 = row.Field<string>("C1"),
C2 = $"{row.Field<string>("C2")}, {string.Join(", ", g.Select(s => s.Field<string>("C2")))}",
C3 = row.Field<int>("C3") + g.Sum(s => s.Field<int>("C3")),
}).FirstOrDefault();
if (q != null)
dt2.Rows.Add(q.C1, q.C2, q.C3);
}
dataGridView1.DataSource = null;
dataGridView1.DataSource = dt2;
The idea behind this snippet is to:
Get the complete rows and iterate through them.
For each complete row, we get it's index from the original DataTable and add 1 to make a starting search point for the incomplete rows. The Skip extension is the method to achieve that.
The TakeWhile extension function gets the incomplete rows and stops at the next complete row.
The GroupBy extension function groups the incomplete rows to concatenate their C2 values and sum their C3 values, add the results to the values of the complete row and create a temporary anonymous object to hold these values.
Extract the anonymous object and add a new DataRow to the output DataTable.
And finally, bind the output DataTable to a DGV.
Happy 2020 for all.
Related
This is something I have spent several hours on and haven't been able to figure it out. Basically, I have a List<object>, and each object in that list has a Dictionary<long, Dictionary<string,string>>. The Dictionary<string,string> is data pair sets that need to be grouped by. Is there a way with linq to iterate through the List<object> with an unknown number/name of Dictionary<string,string> keys and use group by?
Each dictionary is actually a row of data, and the string key value pair is actually column/data, for context. And unfortunately, I cannot change how I am receiving this data.
Most of the examples I can find only seem to work with DataTables, or are hard coded for certain column names. If it is not possible while the dictionaries are inside objects in that list, can it be done if it was just a List<Dictionary<string,string>> ?
For the purpose of this code snippet please assume that the variable AllDataList is the complete list of ContainerClass objects. The routine Projection was borrowed from:
https://www.codeproject.com/Questions/141367/Dynamic-Columns-from-List-using-LINQ
Lastly, unfortunately I cannot bring in third party library's for this.
public class ContainerClass
{
public Dictionary<long, Dictionary<string, string>> data;
public ContainerClass()
{
data = Dictionary<long, Dictionary<string, string>>;
data.add(0,new Dictionary<string,string>());
}
}
private dynamic Projection(object a, IEnumerable<string> props)
{
if (a == null)
{
return null;
}
IDictionary<string, object> res = new System.Dynamic.ExpandoObject();
var type = a.GetType();
foreach (var pair in props.Select(n => new {
Name = n,
Property = type.GetProperty(n)
}))
{
res[pair.Name] = pair.Property.GetValue(a, new object[0]);
}
return res;
}
public void DoStuff()
{
List<string> cols = new List<string>();
//normally cols would be determined at runtime
cols.add("Column1");
cols.add("Column2");
cols.add("Column3");
List<ContainerClass> res = (List<ContainerClass>) this.AllDataList.GroupBy(x => new[] {
Projection(x.data,cols)
}).Select(y =>Projection(y, cols)); //unsure if the select is necessary
}
EDIT: We ended up pursuing a different route
Do you have something like this?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
DataTable dt = new DataTable();
dt.Columns.Add("ID", typeof(long));
dt.Columns.Add("Name", typeof(string));
dt.Columns.Add("Value", typeof(string));
dt.Rows.Add(new object[] { 1, "abc", "mno" });
dt.Rows.Add(new object[] { 1, "def", "mnp" });
dt.Rows.Add(new object[] { 1, "def", "mnq" });
dt.Rows.Add(new object[] { 2, "abc", "mnr" });
dt.Rows.Add(new object[] { 2, "abd", "mns" });
dt.Rows.Add(new object[] { 3, "abe", "mnt" });
dt.Rows.Add(new object[] { 3, "abf", "mnu" });
dt.Rows.Add(new object[] { 4, "abc", "mnv" });
dt.Rows.Add(new object[] { 4, "ade", "mnw" });
Dictionary<long, Dictionary<string, string>> dict = dt.AsEnumerable()
.GroupBy(x => x.Field<long>("ID"), y => y)
.ToDictionary(x => x.Key, y => y
.GroupBy(a => a.Field<string>("Name"), b => b.Field<string>("Value"))
.ToDictionary(a => a.Key, b => b.FirstOrDefault())
);
//Now back to a datatable
DataTable dt2 = new DataTable();
dt2.Columns.Add("Col A", typeof(long));
dt2.Columns.Add("Col B", typeof(string));
dt2.Columns.Add("Col C", typeof(string));
foreach(long id in dict.Keys)
{
foreach (KeyValuePair<string, string> value in dict[id])
{
dt2.Rows.Add(new object[] {id, value.Key, value.Value});
}
}
}
}
}
Here is another method that may work
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
DataTable dt = new DataTable();
dt.Columns.Add("ID", typeof(long));
dt.Columns.Add("Name", typeof(string));
dt.Columns.Add("Value", typeof(string));
dt.Rows.Add(new object[] { 1, "abc", "mno" });
dt.Rows.Add(new object[] { 1, "def", "mnp" });
dt.Rows.Add(new object[] { 1, "def", "mnq" });
dt.Rows.Add(new object[] { 2, "abc", "mnr" });
dt.Rows.Add(new object[] { 2, "abd", "mns" });
dt.Rows.Add(new object[] { 3, "abe", "mnt" });
dt.Rows.Add(new object[] { 3, "abf", "mnu" });
dt.Rows.Add(new object[] { 4, "abc", "mnv" });
dt.Rows.Add(new object[] { 4, "ade", "mnw" });
Dictionary<long, Dictionary<string, List<DataRow>>> dict = dt.AsEnumerable()
.GroupBy(x => x.Field<long>("ID"), y => y)
.ToDictionary(x => x.Key, y => y
.GroupBy(a => a.Field<string>("Name"), b => b)
.ToDictionary(a => a.Key, b => b.ToList())
);
//Now back to a datatable
DataTable dt2 = new DataTable();
foreach(long id in dict.Keys)
{
foreach (KeyValuePair<string, List<DataRow>> value in dict[id])
{
dt2.Merge(value.Value.CopyToDataTable());
}
}
}
}
}
How to group the below data ? as I am looping through the collection and it gives me only 1 row as there is no grouping in place.
I have to group the below records based on Id column and if there are repeating Ids ? I have to populate model with that many rows.
id name trID trName
1 a 5 x
2 b 6 y
2 c 7 z
3 d 8 m
3 e 9 n
4 f 10 0
class DataModel
{
Public int Id{get;set;}
Public string name{get;set;}
Public RepeatedIDs RepeatedIDCollection{get;set;}
}
class RepeatedIDs
{
Public int trId{get;set;}
Public string trname{get;set;}
}
(from DataRow dr in dataTable.Rows
select new IdModel
{
Id = Convert.ToInt32(dr["ID"]),
name = Convert.ToString(dr["name"]),
// need to group the records here and populate below mode with that many rows
RepeatedIDCollection = new List<RepeatedIDs>
{
new RepeatedIDs()
{
trId = Convert.ToInt32(dr["trId"]),
trname = Convert.ToString(dr["trname"]),
}
}
}).ToList();
What you need is:
var query = dataTable.AsEnumerable()
.GroupBy(r => r.Field<int>("ID"))
.Select(grp => new DataModel
{
Id = grp.Key,
name = String.Join(",", grp.Select(t => t.Field<string>("name"))), //Because there could be multiple names
RepeatedIDCollection = grp.Select(t => new RepeatedIDs
{
trId = t.Field<int>("trID"),
trname = t.Field<string>("trName")
}).ToList(),
});
What this query is doing:
Grouping the data based on ID column in DataTable
Later selecting an object of DataModel
The Id in DataModel is the key from group.
There will be multiple names in the grouped data
Later it creates a List<RepeatedIDCollection> by getting the trId and trname from grouped collection.
Make sure you specify the correct types in Field method.
Try following :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
DataTable dt = new DataTable();
dt.Columns.Add("id", typeof (int));
dt.Columns.Add("name", typeof (string));
dt.Columns.Add("trID", typeof (int));
dt.Columns.Add("trName", typeof (string));
dt.Rows.Add(new object[] { 1,"a", 5,"x"});
dt.Rows.Add(new object[] { 2,"b", 6,"y"});
dt.Rows.Add(new object[] { 2,"c", 7,"z"});
dt.Rows.Add(new object[] { 3,"d", 8,"m"});
dt.Rows.Add(new object[] { 3,"e", 9,"n"});
dt.Rows.Add(new object[] { 4,"f", 510,"0"});
var groups = dt.AsEnumerable().GroupBy(x => x.Field<int>("id")).ToList();
}
}
}
My first DataTable is
Name | Value
---------------+----------
A | 12
B | 22
and i want this table as
A | B
---------------+----------
12 | 22
How to resolve this,please help me i tried a lot but i didn't get.Thank You in Advance.
You can convert rows into column using below line of code:
DataTable Pivot(DataTable table, string pivotColumnName)
{
// TODO make sure the table contains at least two columns
// get the data type of the first value column
var dataType = table.Columns[1].DataType;
// create a pivoted table, and add the first column
var pivotedTable = new DataTable();
pivotedTable.Columns.Add("Row Name", typeof(string));
// determine the names of the remaining columns of the pivoted table
var additionalColumnNames = table.AsEnumerable().Select(x => x[pivotColumnName].ToString());
// add the remaining columns to the pivoted table
foreach (var columnName in additionalColumnNames)
pivotedTable.Columns.Add(columnName, dataType);
// determine the row names for the pivoted table
var rowNames = table.Columns.Cast<DataColumn>().Select(x => x.ColumnName).Where(x => x != pivotColumnName);
// fill in the pivoted data
foreach (var rowName in rowNames)
{
// get the value data from the appropriate column of the input table
var pivotedData = table.AsEnumerable().Select(x => x[rowName]);
// make the rowName the first value
var data = new object[] { rowName }.Concat(pivotedData).ToArray();
// add the row
pivotedTable.Rows.Add(data);
}
return pivotedTable;
}
In case you have any problem or query please feel free to ask me.
Here is a pivot table
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
namespace ConsoleApplication33
{
class Program
{
static void Main(string[] args)
{
DataTable dt = new DataTable();
dt.Columns.Add("Name", typeof(string));
dt.Columns.Add("Value", typeof(int));
dt.Columns.Add("Date", typeof(DateTime));
dt.Rows.Add(new object[] {"A", 100, DateTime.Parse("12/1/16")});
dt.Rows.Add(new object[] { "A", 101, DateTime.Parse("12/2/16") });
dt.Rows.Add(new object[] { "A", 102, DateTime.Parse("12/3/16") });
dt.Rows.Add(new object[] { "A", 103, DateTime.Parse("12/4/16") });
dt.Rows.Add(new object[] { "B", 104, DateTime.Parse("12/1/16") });
dt.Rows.Add(new object[] { "B", 110, DateTime.Parse("12/2/16") });
dt.Rows.Add(new object[] { "B", 114, DateTime.Parse("12/3/16") });
dt.Rows.Add(new object[] { "B", 112, DateTime.Parse("12/4/16") });
dt.Rows.Add(new object[] { "B", 100, DateTime.Parse("12/5/16") });
dt.Rows.Add(new object[] { "C", 120, DateTime.Parse("12/1/16") });
dt.Rows.Add(new object[] { "C", 130, DateTime.Parse("12/2/16") });
dt.Rows.Add(new object[] { "C", 140, DateTime.Parse("12/3/16") });
dt.Rows.Add(new object[] { "C", 150, DateTime.Parse("12/4/16") });
dt.Rows.Add(new object[] { "C", 160, DateTime.Parse("12/5/16") });
dt.Rows.Add(new object[] { "C", 101, DateTime.Parse("12/6/16") });
string[] uniqueNames = dt.AsEnumerable().Select(x => x.Field<string>("Name")).Distinct().ToArray();
var groups = dt.AsEnumerable().GroupBy(x => x.Field<DateTime>("Date")).ToList();
DataTable pivot = new DataTable();
pivot.Columns.Add("Date", typeof(DateTime));
foreach (var name in uniqueNames)
{
pivot.Columns.Add(name, typeof(string));
}
foreach (var group in groups)
{
DataRow newRow = pivot.Rows.Add();
newRow["Date"] = group.Key;
foreach (DataRow row in group)
{
newRow[row.Field<string>("Name")] = row.Field<int>("Value");
}
}
}
}
}
I have two tables:
Retailers
Invoices
Retailers has two columns:
1.1. RetailerID
1.2. RetailerName
Invoices has three columns:
2.1. InvoiceID
2.2. InvoiceProfit
2.3. RetailerID
Retailers.RetailerID is linked to Invoices.RetailerID (one-to-many).
What I want to do is write a linq (or in the form of a lambda exp) that returns Retailer.RetailerID, Retailer.RetailerName, Invoice.InvoiceProfit.
I can do this like so:
var retailers = from r in db.Retailers select t;
var invoices = from i in db.Invoices select i;
var retailersAndInvoices = from r in retailers join i in invoices on r.RetailerID equals i.RetailerID select new {t.RetailerName, i.InvoiceProfit};
I want to return only Distinct RetailerNames and the Sum of all Invoices.InvoiceProfit next to each one - the purpose being "Top Ten Retailers"!
How can i do this?
Use GroupBy to convert a flat list to groups by RetailerName
Use Sum(i => i.InvoiceProfit) to compute totals
Use new { ... } to pair up retailers with their profit totals
Use OrderByDescending(p => p.TotalProfit) to get high-profit retailers to the top
Use Take(10) to limit the list to ten items.
Overall query would look like this:
var topTen = retailersAndInvoices
.GroupBy(ri => ri.RetailerName)
.Select(g => new {
Retailer = g.Key
, TotalProfit = g => g.Sum(i => i.InvoiceProfit)
})
.OrderByDescending(p => p.TotalProfit)
.Take(10)
.ToList();
I use combination of lambda and linq. See msdn : https://code.msdn.microsoft.com/LINQ-Join-Operators-dabef4e9
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
namespace ConsoleApplication28
{
class Program
{
static void Main(string[] args)
{
DataTable retailers = new DataTable();
retailers.Columns.Add("RetailerID", typeof(int));
retailers.Columns.Add("RetailerName", typeof(string));
retailers.Rows.Add(new object[] { 123, "abc" });
retailers.Rows.Add(new object[] { 124, "abd" });
retailers.Rows.Add(new object[] { 125, "abe" });
DataTable invoices = new DataTable();
invoices.Columns.Add("InvoiceID", typeof(int));
invoices.Columns.Add("InvoiceProfit", typeof(decimal));
invoices.Columns.Add("RetailerID", typeof(int));
invoices.Rows.Add(new object[] { 100, 200, 123 });
invoices.Rows.Add(new object[] { 101, 201, 123 });
invoices.Rows.Add(new object[] { 102, 202, 123 });
invoices.Rows.Add(new object[] { 103, 203, 123 });
invoices.Rows.Add(new object[] { 104, 204, 124 });
invoices.Rows.Add(new object[] { 105, 205, 124 });
invoices.Rows.Add(new object[] { 106, 206, 124 });
invoices.Rows.Add(new object[] { 107, 207, 125 });
invoices.Rows.Add(new object[] { 108, 208, 125 });
invoices.Rows.Add(new object[] { 109, 209, 125 });
var retailersAndInvoices = (from r in retailers.AsEnumerable()
join i in invoices.AsEnumerable() on r.Field<int>("RetailerID") equals i.Field<int>("RetailerID")
select new { name = r.Field<string>("RetailerName"), profit = i.Field<decimal>("InvoiceProfit") })
.GroupBy(x => x.name)
.Select(x => new { name = x.Key, totalProfit = x.Select(y => y.profit).Sum() }).ToList();
}
}
}
I am pulling some historical data from Firebird database as below:
Product_ID Date Price
1 2001-01-01 10
1 2001-02-01 10
1 2001-03-01 15
1 2001-04-01 10
1 2001-05-01 20
1 2001-06-01 20
What I am trying to do is to extract the first for occurrence every price change.
Example of expected data set:
Product_ID Date Price
1 2001-01-01 10
1 2001-03-01 15
1 2001-04-01 10
1 2001-05-01 20
I know that on MSSQL I could leverage LAG for that. Is it possible to do that with Firebird?
You can try this, but be aware I didn't tested it:
CREATE PROCEDURE SP_Test
RETURNS (
Product_ID INTEGER,
Date DATE,
Price INTEGER
)
AS
DECLARE VARIABLE Last_Product_ID INTEGER;
DECLARE VARIABLE Last_Date DATE;
DECLARE VARIABLE Last_Price INTEGER;
BEGIN
FOR SELECT Product_ID, Date, Price
FROM xxxx
ORDER BY Product_ID, Date
INTO Product_ID, Date, Price
DO BEGIN
IF ((:Last_Product_ID IS NULL) OR
(:Last_Date IS NULL) OR
(:Last_Price IS NULL) OR
(:Product_ID <> :Last_Product_ID) OR
(:Price <> :Last_Price)) THEN
SUSPEND;
Last_Product_ID = :Product_ID;
Last_Date = :Date;
Last_Price = :Price;
END;
END;
in MoreLinq there is a Lag extension method but it is supported only in Linq to Objects...
What you can do, if you are looking for a C# linq answer for that you can:
Basically order your data the correct way and then add a row index for while price (and product_id) is still the same. Then group by it and select the min date.
int groupingIndex = 0;
int previousPrice = 0;
var response = data
.OrderBy(item => item.Product_ID)
.ThenBy(item => item.Date)
.Select(item =>
{
if (item.Price != previousPrice)
{
previousPrice = item.Price;
groupingIndex++;
}
return new { Index = groupingIndex, Item = item };
})
.GroupBy(item => new { item.Index, item.Item.Product_ID, item.Item.Price } )
.Select(group => new Record
{
Product_ID = group.Key.Product_ID,
Price = group.Key.Price,
Date = group.Min(item => item.Item.Date)
}).ToList();
And if you don't mind doing the operation in the C# and not the DB (and using a beta version of the MoreLinq) then:
int index = 0;
var result2 = data
.OrderBy(item => item.Product_ID)
.ThenBy(item => item.Date)
.Lag(1, (current, previous) => new { Index = (current.Price == previous?.Price ? index : ++index), Item = current })
.GroupBy(item => new { item.Index, item.Item.Product_ID, item.Item.Price })
.Select(group => new Record { Product_ID = group.Key.Product_ID, Price = group.Key.Price, Date = group.Min(item => item.Item.Date) })
.ToList();
This is a little complicated but it works
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
namespace ConsoleApplication6
{
class Program
{
static void Main(string[] args)
{
DataTable dt = new DataTable();
dt.Columns.Add("Product_ID", typeof(int));
dt.Columns.Add("Date", typeof(DateTime));
dt.Columns.Add("Price", typeof(int));
dt.Rows.Add(new object[] {1, DateTime.Parse("2001-01-01"), 10});
dt.Rows.Add(new object[] {1, DateTime.Parse("2001-02-01"), 10});
dt.Rows.Add(new object[] {1, DateTime.Parse("2001-03-01"), 15});
dt.Rows.Add(new object[] {1, DateTime.Parse("2001-04-01"), 10});
dt.Rows.Add(new object[] {1, DateTime.Parse("2001-05-01"), 20});
dt.Rows.Add(new object[] {1, DateTime.Parse("2001-06-01"), 20});
dt.Rows.Add(new object[] { 2, DateTime.Parse("2001-01-01"), 10 });
dt.Rows.Add(new object[] { 2, DateTime.Parse("2001-02-01"), 10 });
dt.Rows.Add(new object[] { 2, DateTime.Parse("2001-03-01"), 15 });
dt.Rows.Add(new object[] { 2, DateTime.Parse("2001-04-01"), 10 });
dt.Rows.Add(new object[] { 2, DateTime.Parse("2001-05-01"), 20 });
dt.Rows.Add(new object[] { 2, DateTime.Parse("2001-06-01"), 20 });
dt = dt.AsEnumerable().OrderBy(x => x.Field<DateTime>("Date")).CopyToDataTable();
List<DataRow> results = dt.AsEnumerable()
.GroupBy(g => g.Field<int>("Product_ID"))
.Select(g1 => g1.Select((x, i) => new { row = x, dup = (i == 0) || ((i > 0) && (g1.Skip(i - 1).FirstOrDefault().Field<int>("Price") != g1.Skip(i).FirstOrDefault().Field<int>("Price"))) ? false : true })
.Where(y => y.dup == false).Select(z => z.row)).SelectMany(m => m).ToList();
}
}
}