2011-10-07 35 views
2

我認爲我的問題是一個經典問題,但我無法獲得實施解決方案的正確方法。所以我會在SOF這裏問一下。在集合中表示類似數據的表格的最佳方式

我需要將一些數據導出到一個CSV文件,其中包含表頭和值。爲了獲得我需要迭代一個集合的數據。集合中的每個項目都有一個屬性,其中包含一組鍵/值對。密鑰包含標題。並非每個鍵/值對的集合都具有相同的大小。

當遍歷鍵/值對時,我收集集合中的所有可能的標題。當您收到其他密鑰/值的集合並找到未知密鑰時,此集合將被擴展。發生這種情況時,您需要確保將相應的值寫入CSV文件的正確標題下。我試圖使用標題集合中標題的索引,但我似乎無法使它工作。我曾想過多維數組,鋸齒形數組和字典組合。

對於我的解決方案,我不想在查找右欄時在標題和鍵之間進行字符串比較。這似乎沒有必要。我認爲,兩個循環和索引應該這樣做。

好的,這是我到目前爲止的代碼。這僅適用於外循環中的1個項目,因爲ArrayList一次僅消費一個,而不消費任何給定的索引。當在外部循環的索引12處添加到頭集合的新項目時,循環的值集合還沒有索引12。所以我得到一個索引越界。所以我想創建一個頭像arraylist大小的值arraylist,但這也不起作用。

Private Shared Function GetData(listItems As SPClient.ListItemCollection) As ArrayList 
     'first store all values and make sure keys and values are matched 
     Dim resultsToStore As ArrayList = New ArrayList() 
     Dim headersToStore As ArrayList = New ArrayList() 
     resultsToStore.Add(headersToStore) 
     Dim totalListItems = listItems.Count 
     Dim fieldNotToStore = ConfigurationService.FieldValuesNotToStore 
     Dim displaynames = ConfigurationService.FieldValueDisplayNames 

     For index As Integer = 0 To totalListItems - 1 
      Dim valuesToStore As ArrayList = New ArrayList() 
      Dim item As SPClient.ListItem = listItems(index) 
      Dim fieldValues = item.FieldValues 

      For Each fieldValue In fieldValues 
       If (Not fieldNotToStore.Contains(fieldValue.Key)) Then 'If it is not in this collection is must be stored 
        Dim headerIndex = headersToStore.IndexOf(fieldValue.Key) 'does this key exist in the headersArray 
        If (headerIndex = -1) Then 'If fieldValue.Key is already in the array it doesn't need to be stored again (-1 = no index found) 
         Dim displayname = String.Empty 
         If (displaynames.ContainsKey(fieldValue.Key)) Then 
          displayname = displaynames.Item(fieldValue.Key) 
         Else 
          displayname = fieldValue.Key.ToString 
         End If 
         headerIndex = headersToStore.Add(displayname) '' Add new header 
        End If 
        valuesToStore.Insert(headerIndex, fieldValue.Value.ToString) 'use headerindex to match key an value 
       End If 
      Next 
      resultsToStore.Add(valuesToStore) 
     Next 
     Return resultsToStore 
    End Function 

我認爲這個問題已經解決了像一千倍,所以請善待。

更新:如果你有任何其他(主流)語言的答案比vb.net還好,但我更喜歡vb.net和C#,因爲它們都在.net框架上。

謝謝

回答

0

首先我要感謝Chrissie1的激勵我才能得到答案。對於其他人,這是我的解決方案。

首先我創建了一個類來表示結果csv文件中的每一行。

Public Class MetaDataValue 
    Inherits SortedDictionary(Of Integer, String) 

    ''' <summary> 
    ''' creates a comma seperated string of all data 
    ''' </summary> 
    ''' <returns></returns> 
    ''' <remarks></remarks> 
    Public Overrides Function ToString() As String 
      ' some code to create a comma separated string of all data contained in the sorted dictionary 
    End Function 
End Class 

我用了一個排序的字典,因爲排序上是一個整數的關鍵的所有數據。這樣你就可以確定所有的數據都是在索引上排序的。此外,使用字典提供了一種將數據添加到集合而不會使索引超出邊界錯誤的方法。

之後,您需要一種方法來收集所有的metadataValues。所以我創建了一個類:

''' <summary> 
''' This class respresents a listItemCollection as provided in the constructor as table like data with headers and rows 
''' </summary> 
''' <remarks></remarks> 
Public Class MetadataValuesFactory 
    Private _fieldsNotToStore As List(Of String) = ConfigurationService.FieldValuesNotToStore 
    Private _headers As MetaDataValue = New MetaDataValue() 
    Private _rows As IList(Of MetaDataValue) = New List(Of MetaDataValue) 

    ''' <summary> 
    ''' returns a metaDAtvaleu object that contains all header values 
    ''' </summary> 
    ''' <returns></returns> 
    ''' <remarks></remarks> 

    Public Function GetHeaders() As MetaDataValue 
     Dim result As MetaDataValue = New MetaDataValue() 
     Dim displaynames = ConfigurationService.FieldValueDisplayNames 
     For Each item In Me._headers 
      Dim displayname = String.Empty 
      If (displaynames.ContainsKey(item.Value)) Then 
       result.Add(item.Key, displaynames.Item(item.Value)) 
      Else 
       result.Add(item.Key, item.Value) 
      End If 
     Next 
     Return result 
    End Function 

    ''' <summary> 
    ''' Returns all rows that represent the values 
    ''' </summary> 
    ''' <returns></returns> 
    ''' <remarks></remarks> 
    Public Function GetRows() As IList(Of MetaDataValue) 
     Return _rows 
    End Function 

    ''' <summary> 
    ''' Creates a new metadatavaluesstore with the specified values 
    ''' </summary> 
    ''' <param name="listItems"></param> 
    ''' <remarks></remarks> 

    Sub New(listItems As ListItemCollection) 
     'first store all values and make sure keys and values are matched 
     Dim totalListItems = listItems.Count 

     For index As Integer = 0 To totalListItems - 1 
      Dim valuesToStore As MetaDataValue = New MetaDataValue() 
      Dim item As SPClient.ListItem = listItems(index) 
      Dim fieldValues = item.FieldValues 

      For Each fieldValue In fieldValues 
       If (Not _fieldsNotToStore.Contains(fieldValue.Key)) Then 'If it is not in this collection is must be stored 
        'Get index of field in _headers 
        Dim headerindex As Integer = _headers.Values.ToList().IndexOf(fieldValue.Key.ToString) 
        If (headerindex = -1) Then 'If fieldValue.Key is already in the array it doesn't need to be stored again (-1 = no index found) 
         'If Not exists then store header/key in _headers ánd set previous index to index of new headers value 
         headerindex = _headers.Count '' Add new header 
         _headers.Add(headerindex, fieldValue.Key.ToString) 
        End If 
        'add value to valuesstore 
        Dim valueToStore As String 
        If (fieldValue.Value Is Nothing) Then 
         valueToStore = String.Empty 
        Else 
         valueToStore = fieldValue.Value.ToString 
        End If 
        valuesToStore.Add(headerindex, valueToStore) 
       End If 
      Next 
      _rows.Add(valuesToStore) 
     Next 
End Sub 
End Class 

N.B.我把這稱爲工廠,我不太確定這是使用這種模式的正確方法。但它不能是(普通)模型,因爲這段代碼使用配置服務。你也不能真的稱它爲服務,所以我爲工廠安家落戶。

使用這兩個類可以更容易地使用工廠中包含的數據。例如,在我原來的解決方案中,我用displayname改變了一些值。就像Chrissie1建議的那樣可以重構。正如你所看到的,我現在通過一種獲取所有頭文件的方法來實現這一點。這種方式有點晚了;在內部使用原始值,而在外部只能看到工廠允許的值。這種邏輯包含在工廠內。如果在將來某個時候需要某些新功能,可以很容易地分別訪問標題和值。

的代碼編寫CSV文件現在更加可以理解:

metaDataStreamWriter.WriteLine(metadataFactory.GetHeaders.ToString) 
For Each storeValue In metadataFactory.GetRows 
    metaDataStreamWriter.WriteLine(storeValue.ToString) 
Next 

反正這解決了我的問題。非常感謝您提供反饋意見和所學教訓。如果您有一些意見,請提供。

2

首先我想你應該把你的功能分成更小的塊。其次,你應該問自己爲什麼你仍然在使用ArrayList,而你有泛型玩和使用。

你似乎存儲的ArrayList中的ArrayList這已經是你需要的地方一類,然後像

Public Class ValueStore 
    Public Property Index as Integer 
    Public Property FieldValue as String 
End Class 

該類的名單,然後你可以有一個類ResutlStore這樣

Public Class ResultStore 
    Public ReadOnly Property ValueStores as Ilist(Of ValueStore) 
    Public Sub New() 
    ValueStores = new List(Of ValueStore) 
    End Sub 
    Public Sub AddValueStore(Byval Index as Integer, FieldValue as Integer) 
    ValueStores.Add(new ValueStore() With {.Index = Index, .FieldValue = FieldValue}) 
    End Sub 
End Class 
的標誌

那之後你做一些提取方法

例如,您可以踢這件事出來到它自己的方法

Dim headerIndex = headersToStore.IndexOf(fieldValue.Key) 'does this key exist in the headersArray 
If (headerIndex = -1) Then 'If fieldValue.Key is already in the array it doesn't need to be stored again (-1 = no index found) 
    Dim displayname = String.Empty 
    If (displaynames.ContainsKey(fieldValue.Key)) Then 
     displayname = displaynames.Item(fieldValue.Key) 
    Else 
     displayname = fieldValue.Key.ToString 
    End If 
    headerIndex = headersToStore.Add(displayname) '' Add new header 
End If 

像這樣。

Private Shared Function HeaderIndex(Byval fieldvaluekey as object) as integer 
    Dim displaynames = ConfigurationService.FieldValueDisplayNames 
    Dim headerIndex = headersToStore.IndexOf(fieldValue.Key) 'does this key exist in the headersArray 
     If (headerIndex = -1) Then 'If fieldValue.Key is already in the array it doesn't need to be stored again (-1 = no index found) 
     Dim displayname = String.Empty 
     If (displaynames.ContainsKey(fieldValue.Key)) Then 
      displayname = displaynames.Item(fieldValue.Key) 
     Else 
      displayname = fieldValue.Key.ToString 
     End If 
     headerIndex = headersToStore.Add(displayname) '' Add new header 
     End If 
     return headerIndex 
    End Function 

這將已經讓你的功能更具可讀性

Private Shared Function GetData(listItems As SPClient.ListItemCollection) As ArrayList 
     'first store all values and make sure keys and values are matched 
     Dim resultsToStore As ArrayList = New ArrayList() 
     Dim headersToStore As ArrayList = New ArrayList() 
     resultsToStore.Add(headersToStore) 
     Dim totalListItems = listItems.Count 
     Dim fieldNotToStore = ConfigurationService.FieldValuesNotToStore 

     For index As Integer = 0 To totalListItems - 1 
      Dim valuesToStore As ArrayList = New ArrayList() 
      Dim item As SPClient.ListItem = listItems(index) 
      Dim fieldValues = item.FieldValues 

      For Each fieldValue In fieldValues 
       If (Not fieldNotToStore.Contains(fieldValue.Key)) Then 'If it is not in this collection is must be stored 
        valuesToStore.Insert(headerIndex(fieldValue.Key), fieldValue.Value.ToString) 'use headerindex to match key an value 
       End If 
      Next 
      resultsToStore.Add(valuesToStore) 
     Next 
     Return resultsToStore 
    End Function 
+0

感謝您的評論。我知道這種方法是漫長的。我等待重構,直到後來。我想先工作,但也許你是正確使用班級,可能是一種方式。我使用arraylist,因爲它根據需要擴展。通用列表也是這樣做的,它並沒有真正改變。 – Sigur

+0

仔細查看您的解決方案:1.您的類ValueStore看起來像一本字典,然後ResultStore看起來像一個字典列表。爲什麼不使用這些? 2.即使在您的解決方案中,語句valuesToSore.Insert(....)也會導致錯誤。 – Sigur

+0

我用你的意見作爲靈感。所以謝謝。那麼我現在有更多的OO。非常感謝你的麻煩。 – Sigur

相關問題