2011-10-07 5 views
2

내 문제는 고전적인 문제라고 생각하지만 솔루션을 구현하는 올바른 방법을 얻을 수 없습니다. 그래서 저는 SOF에서 여기서 물어볼 것입니다.컬렉션의 데이터를 표로 표현하는 가장 좋은 방법

표와 같은 헤더와 값이 포함 된 CSV 파일로 일부 데이터를 내보내야합니다. 데이터를 얻으려면 컬렉션을 반복해야합니다. 컬렉션의 각 항목에는 키/값 쌍의 컬렉션이 들어있는 속성이 있습니다. 키에 머리글이 들어 있습니다. 키/값 쌍의 모든 콜렉션이 동일한 크기는 아닙니다.

키/값 쌍을 반복 할 때 컬렉션의 모든 가능한 헤더를 수집합니다. 이 콜렉션은 다른 키/값 콜렉션에 도달하고 알 수없는 키가 발견되면 확장됩니다. 이 경우 CSV 파일의 오른쪽 헤더 아래에 해당 값이 기록되는지 확인해야합니다. 헤더 컬렉션에서 헤더의 인덱스를 사용하려고했지만 작동하지 않을 수 있습니다. 다차원 배열, 가변 배열 및 사전과의 여러 조합에 대해 생각해 보았습니다.

내 솔루션의 경우 오른쪽 열을 찾을 때 머리글과 키 사이의 문자열 비교를하고 싶지 않습니다. 이것은 불필요한 것으로 보인다. 두 개의 루프와 인덱스가 있어야한다고 생각합니다.

좋아요, 여기까지 제 코드는 있습니다. ArrayList는 지정된 인덱스가 아닌 한 번에 하나씩 만 사용하므로 외부 루프의 1 개 항목에 대해서만 작동합니다. 머리글 컬렉션에 대한 새 항목이 외부 루프의 인덱스 12에 추가되면 루프의 values ​​컬렉션에는 아직 인덱스 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

먼저 함수를 더 읽기 쉽게됩니다

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 

. 다른 모든 사람들을 위해, 여기 내 해결책이있다.

먼저 결과 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 

어쨌든이 내 문제를 해결했다. 피드백을 제공하고 교훈을 얻은 것에 대해 다시 한 번 감사드립니다. 의견이 있으시면 알려주십시오.

2

우선 나는 당신이 당신의 기능을 작은 덩어리들로 나누어야한다고 생각합니다. 둘째로 왜 generics가 가지고 노는 동안 ArrayList를 사용하는지 왜 자문해야합니다.

당신은 당신이 당신은 다음이

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 
같은 클래스 ResutlStore을 가질 수 있습니다 다음 어딘가에 클래스와

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

같은 클래스의 목록이 필요 징조가 이미있는 arraylists에 arraylists를 저장하는 것 같다

그 후 당신이 할 몇 가지 추출 방법 당신이 그것을 자신의 방법

0123에이 모든 일을 쫓아 낼 수 있습니다 예를 들어

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 

이렇게하면됩니다.이미 내가 답을 얻기 위해 나에게 영감을 위해 Chrissie1 감사드립니다 모두의

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. 솔루션에서조차도 ToSoSore.Insert (....) 문은 오류를 발생시킵니다. – Sigur

+0

귀하의 의견을 영감으로 사용했습니다. 고마워. 이제는 훨씬 더 많은 OO가 있습니다. 많은 고마워요. – Sigur

관련 문제