Dynamic Linq Query from Datatable for Pivot Table - c#

I have been struggling with this problem for a while and was wondering if I can get some help.
I have the following items:
Datatable inputdata = //Data from large scale SQL query
var rowvals = (from x in reportattrs.AsEnumerable()
where x.Field<long>("COLFORREPORT") == 0 && x.Field<long>("VALUEFIELD") == 0
orderby x.Field<long>("ORDERDISPLAY")
select new { Name = x.Field<object>("FIELDNAME") });
var colvals = (from x in reportattrs.AsEnumerable()
where x.Field<long>("COLFORREPORT") == 1 && x.Field<long>("VALUEFIELD") == 0
orderby x.Field<long>("ORDERDISPLAY")
select new { Name = x.Field<object>("FIELDNAME") });
var datavals = (from x in reportattrs.AsEnumerable()
where x.Field<long>("COLFORREPORT") == 1 && x.Field<long>("VALUEFIELD") == 1
orderby x.Field<long>("ORDERDISPLAY")
select new { Name = x.Field<object>("FIELDNAME") });
Rowvals are the attributes of the table that we want to filter for in rows, colvals are the column values for the pivot table, datavals are the data sums
What I want to do is create my own custom pivot table routine that allows me to filter and layer in a more sophisticated method.
I am able to get the specific distinct values of one row:
foreach(object val in rowvals){
var distinctValues = dsValues.AsEnumerable()
.Select(row => new {
attribute1_name = row.Field<string>(val)
})
.Distinct();
}
How I can filter out for each value in combination (for a variable number rows and cols) is difficult for me. Not only that, I need to select all the values for each value column so I can aggregate it how I see fit.
Any help would be greatly!
Thanks
Jon

It is not absolutely clear how your input data looks like; since you want to build pivot table I assume that it can be presented as dataset like that (if not it should be converted to the tabular view acceptable for pivot operation):
col1 | col2 | val1
-------------------
C1 | R1 | 1
C1 | R2 | 2
C1 | R1 | 3
C2 | R1 | 4
and desired pivot table is something like that (lets use 'col1' values for pivot table columns, and 'col2' values for rows, and SUM of val1 for values):
| C1 | C2
-----------------
R1 | 4 | 4
R2 | 2 | 0 (or empty)
Data for pivot table like that can be easily calculated by NReco PivotData library with just several lines of code (disclaimer: I'm an author of this library):
// group and calculate measures
var pivotData = new PivotData(
new string[] {"col1","col2"},
new SumAggregatorFactory("val1"),
new DataTableReader(t) );
// get pivot table model for accessing columns/rows/values in easy way
var pivotTable = new PivotTable(
new []{"col2"}, // row dimension(s)
new []{"col1"}, // column dimension(s)
pivotData );
var rowLabels = pivotTable.RowKeys;
var colLabels = pivotTable.ColumKeys;
var cellValue = pivotTable[0, 0].Value; // R1 + C1: 4
var grandTotal = pivotTable[null,null].Value; // 10
Also you can specify several columns for rows/columns and can calculate several measures. Feel free to contact me if something is not clear.

Related

Insert values in col1 of list 1 from col 2 of list 2 based on a common column

List 1:
| User Id | Latest |
+---------------------+------------------+
| 1 | 1 |
| 2 | 3 |
| 3 | 3 |
| 4 | 0 |
List 2:
| User Id | Latest | Rating |
+---------------------+------------------+------------------+
| 1 | null | 10 |
| 2 | null | 12 |
| 3 | null | 11 |
| 4 | null | 16 |
I want to insert the values of the Latest column of List1 into the Latest column of List2 based on joining/comparing values of the User Id column in both lists.
I can use a foreach loop but that would run n*m I guess and look ugly. Is there a way to do it with LINQ or efficiently?
Regards.
Junaid
var result = from i1 in List1
join i2 in List2
on i1.UserId equals i2.UserId
select new
{
i2.UserId,
i1.Latest,
i2.Rating
};
you can do it with LINQ :
Try this code :
List2.ForEach(item1 =>
{
item1.Latest = List1.FirstOrDefault(item2 => item2.UserId == item1.UserId)?.Latest;
});
Note That, Latest must be Nullable.
LINQ will never change any of the source sequences, it can only extract data from it.
You will have to enumerate over the extracted data to update your original tables.
var recordsToUpdate = List2.Join(List1, // join List2 and List1
list2Row => list2Row.UserId, // from every row in List2 take UserId
list1Row => list1Row.UserId, // from every row in List1 take UserId
(list2Row, list1Row) => new // when they match make one new object
{
Id = list2Row.UserId, // take UserId from list2
Latest = list1Row.Latest, // take Latest from list1
Rating = list2Row.Rating, // take Rating from list2
})
.ToList(); // execute the query
I don't know how you update your records. Entity framework? SQL? it will be something like this:
foreach (var recordToUpdate in recordsToUpdate)
{
UpdateRecord(recordToUpdate.UserId, recordToUpdate.Latest, recordToUpdate.Rating)
// TODO: implement this function
}
Try something like this. this may fix your issue with adding the Latest value from List1 to List2.
List2.AddRange(List1.Select(user => new List1{
Latest = user.Latest,
UserID = user.UserID
}));

Select data table row based on column with comma separated values

I have a data table that contains one column with comma separated values. I am trying to formulate a filter to use with dt.Select and need some help.
Data table looks something like this:
col1 | col2 | Some_IDs | col4
-----|------|----------|------
a | b | 1,2,3 | g
c | d | 2 | h
e | f | 1, 3 | i
If I am looking for "2", I need rows 1 and 2 returned; if I am looking for 1,3 I need rows 1 and 3 returned.
Thanks.
If you can use Linq then the following should work:
// string filter = can be: "2" , "1,3", etc
var filterArray = filter.Split(',').Select(s => s.Trim());
DataRow[] dataRows = dt.AsEnumerable()
.Where(row=>filterArray.All(f=>row.Field<string>("Some_IDs")
.Split(',')
.Any(v=>v.Trim() == f)))
.ToArray();
The Above will match the rows that contain all filter values.
If you want to rows that matches some of the filter values then use filterArray.Any instead of .All
.Where(row=>filterArray.Any(f=>row.Field<string>("Some_IDs")
.Split(',')
.Any(v=>v.Trim() == f)))

Repeat a row multiple times in a gridview

I have a Gridview with a column called Quantity. Each product has it own quantity. I need to show as many rows as the quantity says.
For example, if I have
Car | 4 | $ 20
I need to show 4 rows like
Car | 1 | 20
I can't find a way of doing this. I am using linq to retrieve the data from the database and that is the DataSource of my Gridview.
You can do it using the linq Range method. Assuming you have a linq source like this (trivial example)
var result =
from r in db.SourceTable
select new { r.Name, r.Quantity, r.Price };
Add this to the query
var result =
from r in db.SourceTable
from s in Enumerable.Range(1, record.Quantity)
select new { r.Name, 1, r.Price };
However, this is probably not supported by entity framework (and probably not in any other ORM) so you may need to call .ToList() or .AsEnumerable() first.

Populating an ObservableCollection of ObservableCollection, delimited by two database columns

My database is as follows :
ID Date Number NumberIWishToRecord
What I wish to do is use a Linq-to-SQL query to populate an ObservableCollection<ObservableCollection<CustomClass>>.
What I want is select only the rows were Number == a given parameter.
ID refers to a person, what I want to do is get all the information about a person and store it in an ObservableCollection, so I will have an ObservableCollection<CustomClass>, with each CustomClass holding information about only one row, and each ObservableCollection<CustomClass> holding information about only one person (recorded on different days).
I then wish to select an ObservableCollection of the ObservableCollection<CustomClass> which will hold information on all people!
So, some sample data :
ID Date Number NumberIWishToRecord
1 27-06-2012 0.1933 25
1 28-06-2012 0.1933 27
1 29-06-2012 0.1933 29
2 14-06-2012 0.1933 412
2 15-06-2012 0.1741 321
So when I run my method, I want to return only the Numbers of the given parameter, in my case I will choose 0.1933.
I then want both rows where ID = 1 to be saved in an ObservableCollection<CustomClass>, and the single row where ID == 2 to be saved in another ObservableCollection<CustomClass>. Then, both of these ObservableCollections will be held in their own ObservableCollection! To illustrate :
ObservableCollection<ObservableCollection<CustomClass>>
ObservableCollection<CustomClass>
1 27-06-2012 0.1933 25
1 28-06-2012 0.1933 27
1 29-06-2012 0.1933 29
ObservableCollection<CustomClass>
2 14-06-2012 0.1933 412
How would I write a query in linq to sql that would do this ?
I'll just write a standard query syntax Linq expression to achieve this, you adapt it for your tables.
var rowsById = new ObservableCollection<ObservableCollection<row>>(
from r in _rows
where r.number == 1.2
group r by r.ID into rowIdGroup
select new ObservableCollection<row>(rowIdGroup));
If you need to convert data from the row into the CustomClass:
var rowsById = new ObservableCollection<ObservableCollection<CustomClass>>(
from r in _rows
where r.number == 1.2
group r by r.ID into rowIdGroup
select new ObservableCollection<CustomClass>(
rowIdGroup.Select(r => new CustomClass
{
ID = r.ID,
Number = r.number // add more
})));
Or if you prefer query syntax in all the expression:
var rowsById = new ObservableCollection<ObservableCollection<CustomClass>>(
from r in _rows
where r.number == 1.2
group r by r.ID into rowIdGroup
select new ObservableCollection<CustomClass>(
from gr in rowIdGroup select new CustomClass
{
ID = gr.ID,
Number = gr.number
}));

LinQ with Count and Where condition

Hihi, I have a table with the following data:
SampleID | SampleKey | SampleData
1 | 1 | abc
1 | 2 | def
2 | 1 | xxx
2 | 3 | yyy
3 | 3 | zzz
3 | 4 | qqq
I would like to retrieve all rows with at least one SampleKey as 3, which should give me
2 | 1 | xxx
2 | 3 | yyy
3 | 3 | zzz
3 | 4 | qqq
both SampleID with 2 and 3 should be returned as they are considered as one pair.
Pls advice how can I achieve this? May thanks!
I would suggest not using Contains, but the built-in Join method for performance reasons..
var keys = source.Where(s => s.SampleKey == 3).Select(s => s.SampleID).Distinct();
var result = source.Join(keys, s => s.SampleID, k => k, (s, k) => s);
var idsToSelect = from x in MyTable where x.SampleKey == 3 select x.SampleID;
var results = from x in MyTable where idsToSelect.Contains(x.SampleID) select x;
Not sure I fully understand the question, but here's my bid:
var results = from r in MyTable
where r.SampleID == 3 || r.SampleKey == 3
select r;
var nResults = results.Count();
Though, I'll be honest, I don't know why your column named ID isn't actually an ID. Never mind, I think I get it now. You're linking the two columns as a unique key (or so I hope).
--
EDIT
Nappy actually had a great solution, and I'm not sure why s/he deleted it. Grabbing all rows with a 3 then rejoining them works perfect.
In the non-SQL-like syntax you could use
var groupsById = MyData.GroupBy(x => x.SampleId);
var groupsThatMatch = groupsById.Where(g => g.Any(x => x.SampleKey == 3));
var allRows = groupsThatMatch.SelectMany(g => g);
i.e group by ID, find the groups that match then flatten those back into rows. I don't know the SQL-like syntax, sorry.
DataTable dt = new System.Data.DataTable();
dt.Columns.Add("SampleID", typeof(Int32));
dt.Columns.Add("SampleKey", typeof(Int32));
dt.Columns.Add("SampleData", typeof(string));
dt.Rows.Add(1, 1, "abc");
dt.Rows.Add(1, 2, "def");
dt.Rows.Add(2, 1, "xxx");
dt.Rows.Add(2, 3, "yyy");
dt.Rows.Add(3, 3, "zzz");
dt.Rows.Add(3, 4, "qqq");
var result = from DataRow myRow in dt.Rows
where (int)myRow["SampleID"] == 3 || (int)myRow["SampleKey"] == 3
select myRow;
You could probably do with:
var result = data.Where(
y => data.Where(x => x.SampleKey == 3)
.Select(x => x.SampleID)
.Contains(y.SampleID));

Categories