表示集合中类似表格的数据的最佳方式
我认为我的问题是一个经典问题,但我无法找到实施解决方案的正确方法。所以我会在SOF这里问这个问题。
我需要将一些数据导出到 CSV 文件,其中包含标题和值(例如表格)。为了获取数据,我需要迭代集合。集合中的每个项目都有一个属性,其中包含键/值对的集合。密钥包含标头。并非每个键/值对集合都具有相同的大小。
当迭代键/值对时,我收集集合中所有可能的标头。当您到达其他键/值集合并且发现未知键时,该集合将被扩展。发生这种情况时,您需要确保相应的值将写入 CSV 文件的正确标题下。我试图使用标头集合中标头的索引,但我似乎无法让它工作。我考虑过多维数组、锯齿状数组以及与字典的几种组合。
对于我的解决方案,我不想在寻找正确的列时在标题和键之间进行字符串比较。这似乎没有必要。我认为两个循环和索引应该可以做到。
好的,这是我到目前为止的代码。这仅适用于外循环中的 1 项,因为 ArrayList 一次仅消耗一项,而不是任何给定的索引。当在外循环中的索引 12 处添加标头集合的新项目时,循环的值集合还没有索引 12。所以我的索引超出了范围。所以我想用标题数组列表的大小创建一个值数组列表,但这也不起作用。
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 框架上。
谢谢
I think my problem is a classic one, but I can't get the right way of implementing the solution. So I will ask it here at SOF.
I need to export some data to a CSV file which contains headers and values like a table. To get the data I need to iterate through a collection. Each item in the collection has a property which contains a collection of key/value pairs. The key contains the header. Not every collection of key/value pairs is of the same size.
When iterating through the key/value pair I collect all possible headers in a collection. This collection will be extended when you get to an other collection of key/values and an unknown key has been found. When this happens you need to make sure that the corresponding value will be written under the right header in the CSV file. I was trying to use the index of the header in the header collection, but I cant seem to get it to work. I have thought about multidimensional array's, jagged arrays and several combinations with dictionaries.
For my solution I don't want to do string comparisons between headers and keys when looking for the right column. This seems unnecessary. Two loops and indices should do it I think.
OK, here's my code I had so far. This only works for 1 item in the outer loop, because ArrayList expends only one at a time and not to any given index. When a new item to the header collection is added at index 12 in the outer loop, the values collection of the loop doesn't have an index 12 yet. So I get an index out of bounds. So I thought creating a values arraylist with the size of the header arraylist, but that doesn't work either.
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
I think this problem has been solved like a thousand times, so please be kind.
Update: If you have an answer in any other (mainstream) language than vb.net, that is OK too, but I prefer vb.net and C# as they are both on the .net framework.
Thanks
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
首先,我认为你应该将你的函数分成更小的块。其次,你应该问自己,当你有泛型可以玩和使用时,为什么你仍然使用 ArrayList。
您似乎将数组列表存储在数组列表中,这已经表明您需要一个类,然后是该类的列表,例如
您可以拥有一个像这样的类 ResutlStore
之后,您执行一些提取方法
例如,您可以踢掉整个事情 像这样进入它自己的方法
。
这已经使你的函数更具可读性
First of all I think you should split up your function into smaller chunks. Second you should ask yourself why you are still using ArrayList while you have generics to play with and use.
You seem to be storing arraylists in arraylists which is already a sign you need a class somewhere and then a list of that class like
You can then have a class ResutlStore like this
After that you do some extract methods
For instance you can kick this whole thing out into it's own method
Like this.
Which will already make your function more readable
首先,我要感谢 Chrissie1 激励我找到答案。对于其他人,这是我的解决方案。
首先,我创建了一个类来表示结果 csv 文件中的每一行。
我使用了排序字典,因为它对整数键上的所有数据进行排序。这样您就可以确保所有数据都在索引上排序。此外,使用字典提供了一种将数据添加到集合中而不会出现索引越界错误的方法。
之后,您需要一种方法来收集所有元数据值。所以我创建了一个类:
注意,我将其称为工厂,我不太确定这是使用此模式的正确方法。但它不能是一个(普通)模型,因为此代码使用配置服务。你也不能真正称其为服务,所以我选择了工厂。
使用这两个类,您可以更轻松地使用工厂中包含的数据。例如,在我原来的解决方案中,我更改了显示名称的一些值。就像 Chrissie1 建议的那样,这可以重构。正如您所看到的,我现在在获取所有标头的方法中执行此操作。这样就有点晚了;在内部使用原始值,而在外部仅显示工厂允许的值。这样,该逻辑就包含在工厂内。如果将来某个时候需要一些新功能,则可以轻松单独访问标头和值。
编写 CSV 文件的代码现在更容易理解了:
无论如何,这解决了我的问题。再次非常感谢您提供反馈并吸取教训。如果您有什么意见请提出。
First of All I want to thank Chrissie1 for inspiring me to get the answer. For everyone else, here's my solution.
First I created a class to represent each line in the result csv file.
I used a sorted dictionary because that sorts all data on the key which is an integer. This way you are sure all data is sorted on the index. Furthermore using a dictionary provides a way of adding data to a collection without getting index out of bounds errors.
After that you need a way to collect all metadataValues. So I created a class:
N.B. I called this a factory I'm not quite sure this is the right way to use this pattern. But it can't be a (ordinary) model because this code uses the configurationservice. You can't really call it a service either, so I settled for factory.
Using these two classes you can much more easily use the data contained in the factory. For example, in my original solution I changed some values with displaynames. Like Chrissie1 suggested this can be refactored. As you can see I now do this in a method that gets all headers. This way it is sort of late bound; internally the original values are used while on the outside only values are visible that the factory will allow. This way this logic is contained within the factory. If, at some point in the future some new functionality is needed, it is easy to access headers and values separately.
The code writing the CSV file is now much more understandable:
Anyway this solved my problem. Many thanks again for providing feedback and for a lesson learned. If you have some comments please provide.