Лучший способ представить таблицу как данные в коллекциях - PullRequest
2 голосов
/ 07 октября 2011

Я думаю, что моя проблема классическая, но я не могу найти правильный способ реализации решения.Поэтому я спрошу об этом здесь, в 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 и C #, так как они оба на.net framework.

Спасибо

Ответы [ 2 ]

2 голосов
/ 07 октября 2011

Прежде всего, я думаю, вы должны разделить свою функцию на более мелкие куски.Во-вторых, вы должны спросить себя, почему вы все еще используете 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
0 голосов
/ 10 октября 2011

Прежде всего, я хочу поблагодарить 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

NB. Я назвал это фабрикой, я не совсем уверен, что это правильный способ использования этого шаблона.Но это не может быть (обычной) моделью, потому что этот код использует службу конфигурации.Вы также не можете назвать это услугой, поэтому я остановился на фабрике.

Используя эти два класса, вы можете намного проще использовать данные, содержащиеся на фабрике.Например, в моем исходном решении я изменил некоторые значения с отображаемыми именами.Как предположил Крисси1, это можно изменить.Как вы можете видеть, я теперь делаю это методом, который получает все заголовки.Таким образом, это своего рода поздняя связь;внутри используются исходные значения, в то время как снаружи видны только значения, разрешенные фабрикой.Таким образом, эта логика содержится внутри фабрики.Если в какой-то момент в будущем потребуются новые функции, легко получить доступ к заголовкам и значениям по отдельности.

Код, пишущий файл CSV, теперь гораздо более понятен:

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

В любом случае это решило мою проблему.Большое спасибо еще раз за предоставление обратной связи и за урок.Если у вас есть комментарии, пожалуйста, предоставьте.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...