سؤال

لدي صفيفان تم إنشاؤهما أثناء تحليل ملف نصي.الأول يحتوي على أسماء الأعمدة، والثاني يحتوي على القيم من الصف الحالي.أحتاج إلى التكرار على كلتا القائمتين مرة واحدة لإنشاء خريطة.الآن لدي ما يلي:

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

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

يعمل هذا بشكل جيد، لكنه لا يرضي تمامًا إحساسي بالأناقة، ويصبح الأمر صعبًا للغاية إذا كان عدد المصفوفات أكبر من اثنين (كما يجب أن أفعل أحيانًا).هل لدى أي شخص لغة أخرى مختصرة؟

هل كانت مفيدة؟

المحلول

وإذا كان هناك نفس العدد من أسماء الأعمدة كما أن هناك عناصر في كل صف، هل يمكن أن لا تستخدم للحلقة؟

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
}

يبدو هذا بسيطًا قدر الإمكان.نعم، تحتاج إلى وضع طريقة الأداة المساعدة في مكان ما، كرمز قابل لإعادة الاستخدام.لا توجد مشكلة في رأيي.الآن لمصفوفات متعددة ...

إذا كانت المصفوفات من أنواع مختلفة، فلدينا مشكلة.لا يمكنك التعبير عن عدد عشوائي من معلمات النوع في تعريف عام للأسلوب/النوع - يمكنك كتابة إصدارات من PairUp لأي عدد تريده من معلمات النوع، تمامًا كما هو الحال في 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];
    }
}

من المحتمل أن يعتبر هذا مبالغة من قبل معظم الناس ;)

يمكنني الاستمرار في طرح جميع أنواع الأفكار، لأكون صادقًا.لكن الأساسيات هي:

  • مع القليل من العمل القابل لإعادة الاستخدام، يمكنك جعل رمز الاتصال أجمل
  • بالنسبة للمجموعات العشوائية من الأنواع، سيتعين عليك إجراء كل عدد من المعلمات (2، 3، 4...) بشكل منفصل نظرًا للطريقة التي تعمل بها الأدوية العامة
  • إذا كنت سعيدًا باستخدام نفس النوع لكل جزء، فيمكنك القيام بعمل أفضل

في لغة وظيفية وكنت عادة ما تجد وظيفة "الرمز البريدي" التي يؤمل أن تكون جزءا من C # 4.0. <وأ href = "http://community.bartdesmet.net/blogs/bart/archive/2008/11/03/c-4-0-feature-focus-part-3-intermezzo-linq-s-new-zip -operator.aspx "يختلط =" نوفولو noreferrer "> بارت دي سميت يوفر تنفيذ مضحك البريدي على أساس وظائف LINQ الحالية:

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 # تفتقر إلى نوع الصفوف (tuple) مناسبة، وهذا هو 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);
}

وبدلا من إنشاء اثنين من صفائف منفصلة يمكن أن تقوم مجموعة ثنائية الأبعاد، أو قاموس (والذي من شأنه أن يكون أفضل). ولكن في الحقيقة، اذا كان يعمل أنا لن محاولة لتغييره.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top