我认为我的问题是经典问题,但我无法找到实施解决方案的正确方法。所以我会在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 :(得分:2)
首先,我认为你应该将你的功能分成更小的块。其次,你应该问问自己,为什么你仍然在使用ArrayList,而你可以使用泛型来使用和使用。
你似乎在arraylists中存储arraylists,这已经是你需要一个类的一个标志,然后是一个类的列表,如
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
答案 1 :(得分: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。我称之为工厂,我不太确定这是使用这种模式的正确方法。但它不能是(普通)模型,因为此代码使用配置服务。你也不能把它称为服务,所以我选择了工厂。
使用这两个类,您可以更轻松地使用工厂中包含的数据。例如,在我的原始解决方案中,我使用displaynames更改了一些值。像Chrissie1建议的那样可以重构。正如您所看到的,我现在在获取所有标头的方法中执行此操作。这种方式有点晚了;在内部使用原始值,而在外部仅显示工厂允许的值。这种方式逻辑包含在工厂内。如果在将来某些时候需要一些新功能,则可以轻松地分别访问标头和值。
编写CSV文件的代码现在更容易理解:
metaDataStreamWriter.WriteLine(metadataFactory.GetHeaders.ToString)
For Each storeValue In metadataFactory.GetRows
metaDataStreamWriter.WriteLine(storeValue.ToString)
Next
无论如何这解决了我的问题。再次感谢您提供反馈和吸取教训。如果您有任何意见,请提供。