문제

텍스트 파일을 구문 분석하는 동안 두 개의 배열이 만들어졌습니다. 첫 번째는 열 이름을 포함하고 두 번째는 현재 행의 값을 포함합니다. 지도를 만들려면 두 목록을 한 번에 반복해야합니다. 지금은 다음과 같습니다.

var currentValues = currentRow.Split(separatorChar);
var valueEnumerator = currentValues.GetEnumerator();

foreach (String column in columnList)
{
    valueEnumerator.MoveNext();
    valueMap.Add(column, (String)valueEnumerator.Current);
}

이것은 잘 작동하지만 내 우아함을 만족 시키지는 않으며, 어레이의 수가 두 개보다 크면 (때때로해야하기 때문에) 실제로 털이 발생합니다. 다른 사람이 Terser 관용구가 있습니까?

도움이 되었습니까?

해결책

각 행에 요소가있는 것과 동일한 열 이름이있는 경우 for 루프를 사용할 수 없습니까?

var currentValues = currentRow.Split(separatorChar);

for(var i=0;i<columnList.Length;i++){
   // use i to index both (or all) arrays and build your map
}

다른 팁

초기 코드에 불쾌한 의사 버그가 있습니다. IEnumerator<T> 확장 IDisposable 그래서 당신은 그것을 처분해야합니다. 반복자 블록에서는 매우 중요 할 수 있습니다! 어레이에 대한 문제는 아니지만 다른 사람과 함께 할 것입니다. IEnumerable<T> 구현.

나는 이것처럼 할 것이다 :

public static IEnumerable<TResult> PairUp<TFirst,TSecond,TResult>
    (this IEnumerable<TFirst> source, IEnumerable<TSecond> secondSequence,
     Func<TFirst,TSecond,TResult> projection)
{
    using (IEnumerator<TSecond> secondIter = secondSequence.GetEnumerator())
    {
        foreach (TFirst first in source)
        {
            if (!secondIter.MoveNext())
            {
                throw new ArgumentException
                    ("First sequence longer than second");
            }
            yield return projection(first, secondIter.Current);
        }
        if (secondIter.MoveNext())
        {
            throw new ArgumentException
                ("Second sequence longer than first");
        }
    }        
}

그런 다음 필요할 때마다 재사용 할 수 있습니다.

foreach (var pair in columnList.PairUp(currentRow.Split(separatorChar),
             (column, value) => new { column, value })
{
    // Do something
}

또는 일반 쌍 유형을 만들고 Pairup 방법에서 투영 매개 변수를 제거 할 수 있습니다.

편집하다:

쌍 유형의 경우 호출 코드는 다음과 같습니다.

foreach (var pair in columnList.PairUp(currentRow.Split(separatorChar))
{
    // column = pair.First, value = pair.Second
}

그것은 당신이 얻을 수있는만큼 단순하게 보입니다. 예, 유틸리티 방법을 재사용 가능한 코드로 어딘가에 넣어야합니다. 내 견해로는 거의 문제가되지 않습니다. 이제 여러 배열의 경우 ...

배열이 다른 유형 인 경우 문제가 있습니다. 일반 메소드/유형 선언에서 임의의 수의 유형 매개 변수를 표현할 수 없습니다. Action 그리고 Func 최대 4 개의 대의원 매개 변수에 대한 대표단 - 그러나 임의적으로 만들 수는 없습니다.

그러나 값이 모두 같은 유형이라면 배열을 기꺼이 고수하는 경우 쉽습니다. (비 배열도 괜찮지 만 미리 검사하는 길이를 수행 할 수는 없습니다.)이를 수행 할 수 있습니다.

public static IEnumerable<T[]> Zip<T>(params T[][] sources)
{
    // (Insert error checking code here for null or empty sources parameter)

    int length = sources[0].Length;
    if (!sources.All(array => array.Length == length))
    {
        throw new ArgumentException("Arrays must all be of the same length");
    }

    for (int i=0; i < length; i++)
    {
        // Could do this bit with LINQ if you wanted
        T[] result = new T[sources.Length];
        for (int j=0; j < result.Length; j++)
        {
             result[j] = sources[j][i];
        }
        yield return result;
    }
}

그런 다음 호출 코드는 다음과 같습니다.

foreach (var array in Zip(columns, row, whatevers))
{
    // column = array[0]
    // value = array[1]
    // whatever = array[2]
}

여기에는 일정량의 복사가 포함됩니다. 물론 매번 배열을 생성합니다. 다음과 같은 다른 유형을 도입하여 변경할 수 있습니다.

public struct Snapshot<T>
{
    readonly T[][] sources;
    readonly int index;

    public Snapshot(T[][] sources, int index)
    {
        this.sources = sources;
        this.index = index;
    }

    public T this[int element]
    {
        return sources[element][index];
    }
}

이것은 아마도 대부분의 사람들에 의해 과잉으로 간주 될 것입니다;)

솔직히 말해서 모든 종류의 아이디어를 계속 생각해 낼 수는 있지만 기본 사항은 다음과 같습니다.

  • 약간의 재사용 가능한 작업으로 호출 코드를 더 좋게 만들 수 있습니다.
  • Generics의 작동 방식으로 인해 각 매개 변수 수 (2, 3, 4 ...)의 각 매개 변수 (2, 3, 4 ...)를 따로 수행해야합니다.
  • 각 부품마다 동일한 유형을 사용하는 것이 행복하다면 더 잘할 수 있습니다.

기능적 언어에서 일반적으로 C#4.0의 일부가되기를 희망하는 "zip"기능을 찾을 수 있습니다. 바트 드 스모트 기존 LINQ 기능을 기반으로 한 재미있는 ZIP 구현을 제공합니다.

public static IEnumerable<TResult> Zip<TFirst, TSecond, TResult>(
  this IEnumerable<TFirst> first, 
  IEnumerable<TSecond> second, 
  Func<TFirst, TSecond, TResult> func)
{
  return first.Select((x, i) => new { X = x, I = i })
    .Join(second.Select((x, i) => new { X = x, I = i }), 
    o => o.I, 
    i => i.I, 
    (o, i) => func(o.X, i.X));
}

그런 다음 할 수 있습니다 :

  int[] s1 = new [] { 1, 2, 3 };
  int[] s2 = new[] { 4, 5, 6 };
  var result = s1.Zip(s2, (i1, i2) => new {Value1 = i1, Value2 = i2});

실제로 배열을 사용하고 있다면 가장 좋은 방법은 아마도 기존을 사용하는 것입니다. for 인덱스가있는 루프. 훌륭하지는 않지만, 내가 아는 한 .NET은 더 나은 방법을 제공하지 않습니다.

코드를 캡슐화 할 수도 있습니다. zip -이것은 일반적인 고차 목록 기능입니다. 그러나 C# 적절한 튜플 유형이 부족한 C# 이것은 상당히 crufty입니다. 당신은 결국 반환 할 것입니다 IEnumerable<KeyValuePair<T1, T2>> 그리 좋지 않습니다.

그건 그렇고, 당신은 정말로 사용하고 있습니까? IEnumerable 대신에 IEnumerable<T> 또는 왜 캐스팅합니까? Current 값?

두 가지 모두에 ienumerator를 사용하십시오

var currentValues = currentRow.Split(separatorChar);
using (IEnumerator<string> valueEnum = currentValues.GetEnumerator(), columnEnum = columnList.GetEnumerator()) {
    while (valueEnum.MoveNext() && columnEnum.MoveNext())
        valueMap.Add(columnEnum.Current, valueEnum.Current);
}

또는 확장 방법을 만듭니다

public static IEnumerable<TResult> Zip<T1, T2, TResult>(this IEnumerable<T1> source, IEnumerable<T2> other, Func<T1, T2, TResult> selector) {
    using (IEnumerator<T1> sourceEnum = source.GetEnumerator()) {
        using (IEnumerator<T2> otherEnum = other.GetEnumerator()) {
            while (sourceEnum.MoveNext() && columnEnum.MoveNext())
                yield return selector(sourceEnum.Current, otherEnum.Current);
        }
    }
}

용법

var currentValues = currentRow.Split(separatorChar);
foreach (var valueColumnPair in currentValues.Zip(columnList, (a, b) => new { Value = a, Column = b }) {
    valueMap.Add(valueColumnPair.Column, valueColumnPair.Value);
}

두 개의 별도 배열을 만드는 대신 2 차원 배열 또는 사전 (더 나은)을 만들 수 있습니다. 그러나 실제로 그것이 작동한다면 나는 그것을 바꾸려고하지 않을 것입니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top