2

In my ASP.NET Project I have a data table named BrowserStats which contains different browser list for different users. I want to group the users by their id and shows the number of access with specific browsers(Chrome,Firefox,IE) using linq.
My Data Table(BrowserStats) is as following:

UserId      |   Browser
----------------------------
1           |   Chrome-32.0
1           |   Chrome-30.0
1           |   Chrome-33.0
1           |   Firefox-20.0
1           |   Firefox-26.0
1           |   Safari 
1           |   IE-9
1           |   IE-10
2           |   Chrome-31.0
2           |   Chrome-32.0
2           |   IE-10
2           |   Firefox-22.0
2           |   Firefox-26.0

My Output Table should be :

UserId      | Chrome |  Firefox |  IE | Others
-----------------------------------------------
1           |   3    |      2   |   2 |     1
2           |   2    |      2   |   1 |     0
  1. How would be the query in linq for this output?
  2. Is linq a faster way or I should write a stored procedure with this query in database and call it from C#?
  3. Is there any good tutorial on Advanced linq queries?
Rahul Singh
  • 21,585
  • 6
  • 41
  • 56
fyasir
  • 2,924
  • 2
  • 23
  • 36

3 Answers3

3

You can use Nested group in LINQ:-

var query = from browser in browesers
                        group browser by browser.UserID into UserGroup
                        from countGroup in
                            (from brow in UserGroup
                             group brow by new
                                 {
                                     Chrome = brow.Browser.Contains("Chrome"),
                                     Firefox = brow.Browser.Contains("Firefox"),
                                     IE = brow.Browser.Contains("IE")
                                 } into test
                                     select new
                                     {
                                         ChromeCount = test.Key.Chrome ? test.Count() : 0,
                                         FirefoxCount = test.Key.Firefox ? test.Count() : 0,
                                         IECount = test.Key.IE ? test.Count() : 0,
                                         OthersCount = (!test.Key.Chrome && !test.Key.Firefox && !test.Key.IE) ? test.Count() : 0
                                     }
                                 )
                        group countGroup by UserGroup.Key;

I have Used the following Type:-

public class BrowserInfo
    {
        public int UserID { get; set; }
        public string Browser { get; set; }
    }

Here is the working Fiddle.

Rahul Singh
  • 21,585
  • 6
  • 41
  • 56
2

first off, I would consider LINQ slow. LINQ is pretty much a clean way to nest array iteration logic. Only use when the CPU must manipulate static data. or data is CPU generated and there is no backing storage.

that's how I think of it anyways. now for the answer:

I have built a database in SQLServer 2012 Express for Demon-Striation purposes (<- Xanth reference). I used your browsers and made imaginary users. the pivot code should be a stored procedure you call from c#. if you are using VS I can edit for a pure VS(2012) solution because I would prefer to use datasets and add a query to the TableAdapter for using a stored procedure. but this should get you 2/3rds the way there

the User Table::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

user table

the Browser Table:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

browser table

the Usage Table Sample (324 row total):::::::::::::::::::::::::::::::::::::::::::::::::::

usage table

the Diagram:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

SQL diagram

The PIVOT (I added this as a stored procedure):::::::::::::::::::::::::::::::::::::::::::

the pivot result

VS 2012 / WPF implementation::::::::::::::::::

after adding the stored procedure, connecting VS to your database, and adding a Dataset to your project. Drag and drop the stored procedure into your dataset. you can also use the stored procedure without the typed dataset generator. See Here

vs dataset

WPF XAML::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

<Window x:Class="WPFPIVOT.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525">
<Grid>
    <DataGrid Name="datagrid" ItemsSource="{Binding}"/>
</Grid>
</Window>

.cs::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;

namespace WPFPIVOT
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
    Pivot pivoted;
    PivotTableAdapters.GetUsageTableAdapter adapter;


    public MainWindow()
    {
        InitializeComponent();

        //this code is the same for WPF and FORMs
        pivoted = new Pivot();
        adapter = new PivotTableAdapters.GetUsageTableAdapter();
        adapter.Fill(pivoted.GetUsage);
        ///////////////////////////////////////////////////////////////


        datagrid.DataContext = pivoted;
    }


}
}

WPF WINDOW:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

(the UserID column defaults to the right most column the select statement could be select p1.UserID,p1[1],... p.[n])

the wpf window

Community
  • 1
  • 1
RadioSpace
  • 953
  • 7
  • 15
  • can you write the linq query with pivot for this solution? – fyasir Oct 16 '14 at 12:35
  • @NutBoltu I could if I really wanted to. but if your interested there are tons of examples out there. check this [SO QUESTION](http://stackoverflow.com/questions/167304/is-it-possible-to-pivot-data-using-linq). – RadioSpace Oct 16 '14 at 12:50
  • OK thanks. I think I solved it :) thanks for sharing the pivot concept. – fyasir Oct 16 '14 at 12:55
  • @NutBoltu if you found the solution you should post the answer here. so you answer your own question. that is fine here in stack. I am curious as to how you solved it anyways. – RadioSpace Oct 28 '14 at 05:03
2

if you want to go with LINQ. it dynamic add column and row to datatable. output is datatable format

DataTable dt = new DataTable();
        dt.Columns.AddRange(new DataColumn[] { new DataColumn("Browser", Type.GetType("System.String")), new DataColumn("userid", Type.GetType("System.String")) });
        dt.Rows.Add(new object[] {"Chrome-32.0","1" });
        dt.Rows.Add(new object[] { "Chrome-32.0", "1" });
        dt.Rows.Add(new object[] { "Firefox-20.0", "1" });
        dt.Rows.Add(new object[] { "Firefox-26.0", "1" });
        dt.Rows.Add(new object[] { "Safari", "1" });
        dt.Rows.Add(new object[] { "IE-9", "1" });
        dt.Rows.Add(new object[] { "IE-10", "1" });
        dt.Rows.Add(new object[] { "Chrome-31.0", "2" });
        dt.Rows.Add(new object[] { "Chrome-32.0", "1" });
        dt.Rows.Add(new object[] { "IE-10", "1" });
        dt.Rows.Add(new object[] { "Firefox-22.0", "2" });

        DataTable dtOutPut = new DataTable();
        dtOutPut.Columns.Add(new DataColumn("UserID", Type.GetType("System.String")));
        var tableColumnName = dt.AsEnumerable().Select(s => s.Field<string>("Browser").Trim().ToLower().Split('-')[0].Trim()).Distinct();
        foreach (var item in tableColumnName)
        {
            dtOutPut.Columns.Add(new DataColumn(item, Type.GetType("System.String")));
        }

        var usrid = dt.AsEnumerable().Select(s => s.Field<string>("userid").Trim()).Distinct();

        Parallel.ForEach(usrid, (s) => {

            DataRow rec = dtOutPut.NewRow();
            rec["UserID"] = s;
            foreach (var item in tableColumnName)
            {
                rec[item] = dt.AsEnumerable().Where(s1 => s1.Field<string>("Browser").Trim().ToLower().Contains(item) && s1.Field<string>("userid") == s).Count();

            }

            dtOutPut.Rows.Add(rec);
        });

output:

enter image description here

  • Actually the database is from MS-SQL.I need one linq query for the output table. – fyasir Oct 16 '14 at 07:33
  • WOW! this is great! never thought to use the t-sql{`declare @tablename table(col1 type,col2 type)`} outside of SQL using DataTables. this is a good bridge between my answer and Rahul's answer. if all three could be utilized together, I think a really slick and powerful method could be produced for this scenario. – RadioSpace Oct 28 '14 at 05:10