.Net List(Of Type) Weird Issue...

kevp75
kevp75 used Ask the Experts™
on
So, I am trying to develop myself a wrapper around the Google .Net API.

I am able to get all the data I am after, so that part is working as it is supposed to be.

The problem I am encountering is how I can format the data returned into something like a table...  where I get x number of records return, and x number of columns per row.

For instance, in my example, I am getting 2 "records" with 4 columns each.

However, when I try to do what I am doing and write it out to a page, I get 8 records, each one on it's own line.  With only one column each line, looking something like this:

United States::0
Chicopee:::0
::Windows:0
:::2
:United States::0
Weymouth:::0
::Windows:0
:::1

How I want it to look is:

United States : Chicopee : Windows : 2
United States : Weymouth: Windows : 1

Here is the code for the test page:
 
And the AnalyticsInfo class:
 
Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
Most Valuable Expert 2012
Top Expert 2014

Commented:
May be line returns are being added at the end of fields so try

Response.Write(i.City.Trim.Trim(VBCRLF).Trim & ":" & i.Country.Trim.Trim(VBCRLF).Trim & ":" & i.OperatingSystem.Trim.Trim(VBCRLF).Trim & ":" & i.Visits.Trim.Trim(VBCRLF).Trim & "<br />")

Author

Commented:
no change
Most Valuable Expert 2012
Top Expert 2014

Commented:
Try with other characters such as VBCR or VBLF.
Bootstrap 4: Exploring New Features

Learn how to use and navigate the new features included in Bootstrap 4, the most popular HTML, CSS, and JavaScript framework for developing responsive, mobile-first websites.

Author

Commented:
The problem is, what is returned is 8 rows.  I have verified this by debugging and hovering over _Qry.Count and _Qry in the test page.  There should be only 2 rows with 4 columns each.

So there is something funky with how I am doing the Lists...
Most Valuable Expert 2012
Top Expert 2014

Commented:
I think the problem is in ProcessEntries sub where you are adding to the list. You are ADDING each field to the list. I think you need to declare an object on top and set its properties in the CASE statement and add it to the list at the end.

Author

Commented:
example?

Author

Commented:
I think the problem is in ProcessEntries sub where you are adding to the list. You are ADDING each field to the list. I think you need to declare an object on top and set its properties in the CASE statement and add it to the list at the end.

But isn't this what I am already doing?   I am adding the columns to the _IList and then adding that collection to _List in GetAnalyticInfo()???
Most Valuable Expert 2012
Top Expert 2014

Commented:
May be. Difficult for me to see the flow of the program. Stepping through the code may help.

Author

Commented:
Stepping over the code:
Both lists get created and populated.

_IList in ProcessEntries gets 4 values added to it, twice (based on the 2 originating records)
_List in GetAnalyticInfo gets added to twice (see the line above)

Yet, 8 records are produced when I query the GetAnalyticInfo

Author

Commented:
It's almost as if, instead of adding a list to a list, the code is appending the 2ndary list to the originating list

Author

Commented:
how would I go about adding these values to a datatable?
Shahan AyyubSenior Software Engineer

Commented:
Hi!

What you see in _Qry if do noot call .ToList() in this line:

 Dim _Qry = (From n In .GetAnalyticInfo()
                     Select n.Country, n.City, n.OperatingSystem, n.Visits).ToList()

It would be easy to manage for you if you uses Datatable.

Author

Commented:
'In-Memory Query' (expanding will process the collection)

When I expand it:
2 Objects, containing 4 items

I switched things up a little bit, CodeChanger made a statemement that made me realize that all I was doing was adding an item to a list.  Now what I am doing is adding a list to a list...

In GetAnalyticsInfo is now of type: List(Of List(Of Analytics))

I agree, it would be alot easier to manage in a datatable, however, I am not sure how I would go about converting this to a datatable...

Author

Commented:
my return is still not right.  I'll post new code tomorrow

Author

Commented:
Public Function GetAnalyticInfo() As List(Of List(Of Analytics))
        Try
            Dim _List As New List(Of List(Of Analytics))
            If GoogleAnalytics.Login.Login() Then
                Dim _dims As New StringBuilder()
                Dim _mets As New StringBuilder
                For Each item In MetricsAndDimensions.Dimensions
                    _dims.Append(item.ToString() & ",")
                Next
                For Each item In MetricsAndDimensions.Metrics
                    _mets.Append(item.ToString() & ",")
                Next
                With _Q
                    .Ids = "ga:" & _PI
                    .Dimensions = Left(_dims.ToString(), _dims.Length - 1)
                    .Metrics = Left(_mets.ToString(), _mets.Length - 1)
                    .NumberToRetrieve = _RTR
                    .GAStartDate = _SD
                    .GAEndDate = _ED
                End With
                _AFeed = SharedProperties.AnalyticsService.Query(_Q)
                Dim _Qry = (From n In _AFeed.Entries.AsParallel()
                           Select n).ToList()
                If _Qry.Count > 0 Then
                    Dim _TL
                    For Each _Entry In _Qry
                        _TL = ProcessEntries(TryCast(_Entry, DataEntry))
                        _List.Add(_TL)
                    Next
                    Return _List
                Else
                    Return Nothing
                End If
            Else
                Return Nothing
            End If
        Catch ex As Exception
            Return Nothing
        End Try
    End Function

    ''' <summary>
    ''' Process Our Entries
    ''' </summary>
    ''' <param name="_Entries"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Function ProcessEntries(ByVal _Entries As DataEntry) As IList(Of Analytics)
        Dim _IList As New List(Of Analytics)
        For Each _dim As Dimension In _Entries.Dimensions
            Select Case _dim.Name.ToLower()
                Case "ga:browser"
                    _IList.Add(New Analytics() With {.Browser = _dim.Value})
                Case "ga:browserversion"
                    _IList.Add(New Analytics() With {.BrowserVersion = _dim.Value})
                Case "ga:city"
                    _IList.Add(New Analytics() With {.City = _dim.Value})
                Case "ga:country"
                    _IList.Add(New Analytics() With {.Country = _dim.Value})
                Case "ga:date"
                    _IList.Add(New Analytics() With {.Date = _dim.Value})
                Case "ga:latitude"
                    _IList.Add(New Analytics() With {.Latitude = _dim.Value})
                Case "ga:longitude"
                    _IList.Add(New Analytics() With {.Longitude = _dim.Value})
            End Select
        Next
        For Each _met As Metric In _Entries.Metrics
            Select Case _met.Name.ToLower()
                Case "ga:bounces"
                    _IList.Add(New Analytics() With {.Bounces = _met.Value})
                Case "ga:newvisits"
                    _IList.Add(New Analytics() With {.NewVisits = _met.Value})
                Case "ga:pageviews"
                    _IList.Add(New Analytics() With {.PageViews = _met.Value})
                Case "ga:timeonpage"
                    _IList.Add(New Analytics() With {.TimeOnPage = _met.Value})
                Case "ga:timeonsite"
                    _IList.Add(New Analytics() With {.TimeOnSite = _met.Value})
                Case "ga:uniquepageviews"
                    _IList.Add(New Analytics() With {.UniquePageViews = _met.Value})
                Case "ga:visitors"
                    _IList.Add(New Analytics() With {.Visitors = _met.Value})
                Case "ga:visits"
                    _IList.Add(New Analytics() With {.Visits = _met.Value})
            End Select
        Next
        Return _IList
    End Function


test.aspx.vb:
        If Request.ServerVariables("REQUEST_METHOD") = "POST" Then
            Using ga As New GoogleAnalytics.AnalyticsInfo
                With ga
                    .ProfileID = Request.Form("g")
                    .RecordsToRetrieve = 5
                    .StartDate = "2011-07-05"
                    .EndDate = "2011-08-05"
                    Dim _Qry = (From n In .GetAnalyticInfo()
                                Select n)
                    Response.Write(_Qry.Count & " Records<br />")
                    For Each k In _Qry
                        Dim _Qry2 = (From n In k
                                     Select n.Bounces, n.Browser, n.BrowserVersion, n.City, n.Country,
                                     n.Date, n.Latitude, n.Longitude, n.NewVisits, n.PageViews, n.TimeOnPage,
                                     n.TimeOnSite, n.UniquePageViews, n.Visitors, n.Visits).ToList()
                        For Each i In _Qry2
                            Response.Write("---City: " & i.City & " ---Country: " & i.Country & " ---Browser: " & i.Browser & " ---Visits: " & i.Visits)
                        Next
                        Response.Write("<br />")
                    Next

                End With
            End Using
        End If

Open in new window


Is now returning a string like this:
2 Records
---City: ---Country: ---Browser: Firefox ---Visits: 0---City: ---Country: ---Browser: ---Visits: 0---City: Chicopee ---Country: ---Browser: ---Visits: 0---City: ---Country: United States ---Browser: ---Visits: 0---City: ---Country: ---Browser: ---Visits: 0---City: ---Country: ---Browser: ---Visits: 0---City: ---Country: ---Browser: ---Visits: 0---City: ---Country: ---Browser: ---Visits: 0---City: ---Country: ---Browser: ---Visits: 0---City: ---Country: ---Browser: ---Visits: 0---City: ---Country: ---Browser: ---Visits: 0---City: ---Country: ---Browser: ---Visits: 0---City: ---Country: ---Browser: ---Visits: 0---City: ---Country: ---Browser: ---Visits: 0---City: ---Country: ---Browser: ---Visits: 2
---City: ---Country: ---Browser: Internet Explorer ---Visits: 0---City: ---Country: ---Browser: ---Visits: 0---City: Weymouth ---Country: ---Browser: ---Visits: 0---City: ---Country: United States ---Browser: ---Visits: 0---City: ---Country: ---Browser: ---Visits: 0---City: ---Country: ---Browser: ---Visits: 0---City: ---Country: ---Browser: ---Visits: 0---City: ---Country: ---Browser: ---Visits: 0---City: ---Country: ---Browser: ---Visits: 0---City: ---Country: ---Browser: ---Visits: 0---City: ---Country: ---Browser: ---Visits: 0---City: ---Country: ---Browser: ---Visits: 0---City: ---Country: ---Browser: ---Visits: 0---City: ---Country: ---Browser: ---Visits: 0---City: ---Country: ---Browser: ---Visits: 1

When it should be returning a string like:
---City: Chicopee ---Country: United States ---Browser: Firefox ---Visits: 2
---City: Weymouth ---Country: United States ---Browser: Internet Explorer ---Visits: 1

Author

Commented:
anyone anyone...  Bueller, Bueller?
Corey ScheichDeveloper

Commented:
Try Response.WriteLine instead of Response.Write.   WriteLine automatically adds the carriage returns to the end of the line.
Top Expert 2015

Commented:
I do not have the definition of Analytics, so I cannot give you the exact code.

But I have reproduced the something like this of you original post in a List (Of String) and formatted it to How I want it to look is.

Here is thus the solution applied to a collection of Strings. Simply translate the algorithm to your own list:

Dim line As String
Dim _List As New Generic.List(Of String)

'First recreate your data
_list.Add("United States::0")
_list.Add("Chicopee:::0")
_list.Add("::Windows:0")
_list.Add(":::2")
_list.Add(":United States::0")
_list.Add("Weymouth:::0")
_list.Add("::Windows:0")
_List.Add(":::1")

'Then format it

'Get rid of everything you do not need
For x As Integer = 0 To _List.Count - 1
   _List(x) = _List(x).TrimStart(":"c)
   _List(x) = _List(x).TrimEnd("0"c)
   _List(x) = _List(x).TrimEnd(":"c)
Next

'Group them in 1 string by group of 4, getting rid of the leading : that are always superfluous
For x As Integer = 0 To _List.Count - 1 Step 4
   line = _List(x) & " : " & _List(x + 1) & " : " & _List(x + 2) & " : " & _List(x + 3)
   Debug.WriteLine(line)
Next

Open in new window


Since it does a lot of String manipulations, it could be slow if you have a lot of data. In such a case, you could perform the Trims and concatenations with a System.Text.StringBuilder object that should speed up things a lot.

Author

Commented:
yeah, looks like my code dissappeared...  here it is again:
 
Imports Google.GData.Analytics
Imports System.Text
Imports System.Threading.Tasks

Public Class AnalyticsInfo
    Implements IDisposable

#Region "   Properties  "

    Private _AFeed As DataFeed
    Private _Q As DataQuery = New DataQuery("https://www.google.com/analytics/feeds/data")

    ''' <summary>
    ''' Set our Profile ID
    ''' </summary>
    ''' <value></value>
    ''' <remarks></remarks>
    Public WriteOnly Property ProfileID As Integer
        Set(ByVal value As Integer)
            _PI = DataTyping.IsNull(Of Integer)(value, 0)
        End Set
    End Property
    Private _PI As Integer

    ''' <summary>
    ''' Set how many records we should retrieve
    ''' </summary>
    ''' <value></value>
    ''' <remarks></remarks>
    Public WriteOnly Property RecordsToRetrieve As Integer
        Set(ByVal value As Integer)
            _RTR = DataTyping.IsNull(Of Integer)(value, 10)
        End Set
    End Property
    Private _RTR As Integer

    ''' <summary>
    ''' Set the Start Date
    ''' </summary>
    ''' <value></value>
    ''' <remarks></remarks>
    Public WriteOnly Property StartDate As String
        Set(ByVal value As String)
            Dim _tmp = DataTyping.IsNull(Of Date)(value, Date.Now.AddDays(-7))
            _SD = String.Format("{0:yyyy-MM-dd}", _tmp)
        End Set
    End Property
    Private _SD As String = String.Format("{0:yyyy-MM-dd}", Date.Now.AddDays(-7))

    ''' <summary>
    ''' Set the End Date
    ''' </summary>
    ''' <value></value>
    ''' <remarks></remarks>
    Public WriteOnly Property EndDate As String
        Set(ByVal value As String)
            Dim _tmp = DataTyping.IsNull(Of Date)(value, Date.Now)
            _ED = String.Format("{0:yyyy-MM-dd}", _tmp)
        End Set
    End Property
    Private _ED As String = String.Format("{0:yyyy-MM-dd}", Date.Now)

#End Region

    Public Function GetAnalyticInfo() As List(Of List(Of Analytics))
        Try
            Dim _List As New List(Of List(Of Analytics))
            If GoogleAnalytics.Login.Login() Then
                Dim _dims As New StringBuilder()
                Dim _mets As New StringBuilder
                For Each item In MetricsAndDimensions.Dimensions
                    _dims.Append(item.ToString() & ",")
                Next
                For Each item In MetricsAndDimensions.Metrics
                    _mets.Append(item.ToString() & ",")
                Next
                With _Q
                    .Ids = "ga:" & _PI
                    .Dimensions = Left(_dims.ToString(), _dims.Length - 1)
                    .Metrics = Left(_mets.ToString(), _mets.Length - 1)
                    .NumberToRetrieve = _RTR
                    .GAStartDate = _SD
                    .GAEndDate = _ED
                End With
                _AFeed = SharedProperties.AnalyticsService.Query(_Q)
                Dim _Qry = (From n In _AFeed.Entries.AsParallel()
                           Select n).ToList()
                If _Qry.Count > 0 Then
                    Dim _TL
                    For Each _Entry In _Qry
                        _TL = ProcessEntries(TryCast(_Entry, DataEntry))
                        _List.Add(_TL)
                    Next
                    Return _List
                Else
                    Return Nothing
                End If
            Else
                Return Nothing
            End If
        Catch ex As Exception
            Return Nothing
        End Try
    End Function

    ''' <summary>
    ''' Process Our Entries
    ''' </summary>
    ''' <param name="_Entries"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Function ProcessEntries(ByVal _Entries As DataEntry) As IList(Of Analytics)
        Dim _IList As New List(Of Analytics)
        For Each _dim As Dimension In _Entries.Dimensions
            Select Case _dim.Name.ToLower()
                Case "ga:browser"
                    _IList.Add(New Analytics() With {.Browser = _dim.Value})
                Case "ga:browserversion"
                    _IList.Add(New Analytics() With {.BrowserVersion = _dim.Value})
                Case "ga:city"
                    _IList.Add(New Analytics() With {.City = _dim.Value})
                Case "ga:country"
                    _IList.Add(New Analytics() With {.Country = _dim.Value})
                Case "ga:date"
                    _IList.Add(New Analytics() With {.Date = _dim.Value})
                Case "ga:latitude"
                    _IList.Add(New Analytics() With {.Latitude = _dim.Value})
                Case "ga:longitude"
                    _IList.Add(New Analytics() With {.Longitude = _dim.Value})
            End Select
        Next
        For Each _met As Metric In _Entries.Metrics
            Select Case _met.Name.ToLower()
                Case "ga:bounces"
                    _IList.Add(New Analytics() With {.Bounces = _met.Value})
                Case "ga:newvisits"
                    _IList.Add(New Analytics() With {.NewVisits = _met.Value})
                Case "ga:pageviews"
                    _IList.Add(New Analytics() With {.PageViews = _met.Value})
                Case "ga:timeonpage"
                    _IList.Add(New Analytics() With {.TimeOnPage = _met.Value})
                Case "ga:timeonsite"
                    _IList.Add(New Analytics() With {.TimeOnSite = _met.Value})
                Case "ga:uniquepageviews"
                    _IList.Add(New Analytics() With {.UniquePageViews = _met.Value})
                Case "ga:visitors"
                    _IList.Add(New Analytics() With {.Visitors = _met.Value})
                Case "ga:visits"
                    _IList.Add(New Analytics() With {.Visits = _met.Value})
            End Select
        Next
        Return _IList
    End Function

#Region "IDisposable Support"
    Private disposedValue As Boolean ' To detect redundant calls

    ' IDisposable
    Protected Overridable Sub Dispose(ByVal disposing As Boolean)
        If Not Me.disposedValue Then
            If disposing Then
            End If
            _Q = Nothing
            _AFeed = Nothing
        End If
        Me.disposedValue = True
    End Sub

    ' This code added by Visual Basic to correctly implement the disposable pattern.
    Public Sub Dispose() Implements IDisposable.Dispose
        ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
        Dispose(True)
        GC.SuppressFinalize(Me)
    End Sub

#End Region

End Class

Open in new window

Developer
Commented:
The problem that is causing each entry to only have one param is because of your Process Entries method, it is creating a new Analytics object for each entry.  I don't know exactly how the incoming data is separated but it should look something like below assuming the incoming entries are repeating index offsets like below where every browser entry signifies a new output line
_Entries.Dimensions(0).Name = "ga:browser"
_Entries.Dimensions(1).Name = "ga:browserversion"
_Entries.Dimensions(2).Name = "ga:counter"
....etc
_Entries.Dimensions(7).Name = "ga:browser"
_Entries.Dimensions(8).Name = "ga:browserversion"
_Entries.Dimensions(9).Name = "ga:counter"
....etc















Dim _IList As New List(Of Analytics)
        Dim _CurrentAnalytics as Analytics
        For Each _dim As Dimension In _Entries.Dimensions
            Select Case _dim.Name.ToLower()
                Case "ga:browser"
                   _CurrentAnalytics =  _IList.Add(New Analytics() With {.Browser = _dim.Value})
                Case "ga:browserversion"
                    _CurrentAnalytics.BrowserVersion = _dim.Value
                Case "ga:city"
                    _CurrentAnalytics.City = _dim.Value
                Case "ga:country"
                    _CurrentAnalytics.Country = _dim.Value
                Case "ga:date"
                    _CurrentAnalytics.Date = _dim.Value
                Case "ga:latitude"
                    _CurrentAnalytics.Latitude = _dim.Value
                Case "ga:longitude"
                    _CurrentAnalytics.Longitude = _dim.Value
            End Select
        Next
        'add something similar for the Metrics

Open in new window

Author

Commented:
now I get nothing added to the ILIst I am trying to return from that function
Corey ScheichDeveloper
Commented:
If this line ever runs you should have atleast a count of 1, depending on the order of entries you may have to add the item in a different area, or outside the loop.  Also step through the code which case is run first

_CurrentAnalytics =  _IList.Add(New Analytics() With {.Browser = _dim.Value})

Author

Commented:
Expression does not produce a value error on that line
Corey ScheichDeveloper

Commented:
My bad I miss read the line  I thought you used AddNew which returns a value it would then have to be this

_CurrentAnalytics =  New Analytics() With {.Browser = _dim.Value}
_IList.Add(_CurrentAnalytics)

Author

Commented:
this seems to work...

gimme a few to test the rest of it
 
Private Function ProcessEntries(ByVal _Entries As DataEntry) As IList(Of Analytics)
        Dim _IList As New List(Of Analytics)
        Dim _CurA As New Analytics
        With _CurA
            For Each _dim As Dimension In _Entries.Dimensions
                Select Case _dim.Name.ToLower()
                    Case "ga:browser"
                        .Browser = _dim.Value
                    Case "ga:browserversion"
                        .BrowserVersion = _dim.Value
                    Case "ga:city"
                        .City = _dim.Value
                    Case "ga:country"
                        .Country = _dim.Value
                    Case "ga:date"
                        .Date = _dim.Value
                    Case "ga:latitude"
                        .Latitude = _dim.Value
                    Case "ga:longitude"
                        .Longitude = _dim.Value
                End Select
            Next
            For Each _met As Metric In _Entries.Metrics
                Select Case _met.Name.ToLower()
                    Case "ga:bounces"
                        .Bounces = _met.Value
                    Case "ga:newvisits"
                        .NewVisits = _met.Value
                    Case "ga:pageviews"
                        .PageViews = _met.Value
                    Case "ga:timeonpage"
                        .TimeOnPage = _met.Value
                    Case "ga:timeonsite"
                        .TimeOnSite = _met.Value
                    Case "ga:uniquepageviews"
                        .UniquePageViews = _met.Value
                    Case "ga:visitors"
                        .Visitors = _met.Value
                    Case "ga:visits"
                        .Visits = _met.Value
                End Select
            Next
            _IList.Add(New Analytics() With {
               .Browser = _CurA.Browser})
        End With

        Return _IList
    End Function

Open in new window

Corey ScheichDeveloper

Commented:
You want  _IList.Add(New Analytics() With {
               .Browser = _CurA.Browser}) to instead be
 _IList.Add(_CurA)

Otherwise you are only filling the list with the name of the browser, and all the work done to fill the rest of _CurA's values is wasted

Author

Commented:
Actually...  I changed it to:
            _IList.Add(New Analytics() With {
               .Browser = _CurA.Browser,
               .BrowserVersion = _CurA.BrowserVersion,
               .City = _CurA.City,
               .Country = _CurA.Country,
               .Date = _CurA.Date,
               .Latitude = _CurA.Latitude,
               .Longitude = _CurA.Longitude,
               .Bounces = _CurA.Bounces,
               .NewVisits = _CurA.NewVisits,
               .PageViews = _CurA.PageViews,
               .TimeOnPage = _CurA.TimeOnPage,
               .TimeOnSite = _CurA.TimeOnSite,
               .UniquePageViews = _CurA.UniquePageViews,
               .Visitors = _CurA.Visitors,
               .Visits = _CurA.Visits})

But that will work just as well :)

thanks for the help with this!

Author

Commented:
thanks for the help
Corey ScheichDeveloper

Commented:
fyi  Creating and filling a new object (Which is what you said you are currently doing)
            _IList.Add(New Analytics() With {
               .Browser = _CurA.Browser,
               .BrowserVersion = _CurA.BrowserVersion,
               .City = _CurA.City,
               .Country = _CurA.Country,
               .Date = _CurA.Date,
               .Latitude = _CurA.Latitude,
               .Longitude = _CurA.Longitude,
               .Bounces = _CurA.Bounces,
               .NewVisits = _CurA.NewVisits,
               .PageViews = _CurA.PageViews,
               .TimeOnPage = _CurA.TimeOnPage,
               .TimeOnSite = _CurA.TimeOnSite,
               .UniquePageViews = _CurA.UniquePageViews,
               .Visitors = _CurA.Visitors,
               .Visits = _CurA.Visits})

is more CPU and Memory usage than simply passing a pointer to an existing object.
 _IList.Add(_CurA)

Passing the pointer will be better overall performance

Author

Commented:
aye.   switched it already ;)   I sat watching the CPU for a minute while it was running   =)

thanks again!

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial