21

C# で Enumerable から正確に x 値を取得する最良の方法は何ですか? Enumerable .Take() を次のように使用する場合:

var myList = Enumerable.Range(0,10);
var result = myList.Take(20);

結果には 10 個の要素しかありません。

不足しているエントリをデフォルト値で埋めたい。このようなもの:

var myList = Enumerable.Range(0,10);
var result = myList.TakeOrDefault(20, default(int));  //Is there anything like this?

C# にそのような関数はありますか?そうでない場合、これを達成するための最良の方法は何でしょうか?

4

7 に答える 7

19

次のようなことができます:

var result = myList.Concat(Enumerable.Repeat(default(int), 20)).Take(20); 

そして、これを拡張メソッドに変えるのは簡単です:

public static IEnumerable<T> TakeOrDefault<T>(this IEnumerable<T> list, int count, T defaultValue)
{
    return  list.Concat(Enumerable.Repeat(defaultValue, count)).Take(count);
}

しかし、ここには微妙な落とし穴があります。これは、値の型、参照型の場合は完全に正常に機能します。null でない場合は、同じオブジェクトを複数回defaultValue追加しています。これはおそらくあなたが望んでいないことです。たとえば、次のような場合:

var result = myList.TakeOrDefault(20, new Foo());

コレクションのパディングにの同じインスタンスFooを追加します。この問題を解決するには、次のようなものが必要です。

public static IEnumerable<T> TakeOrDefault<T>(this IEnumerable<T> list, int count, Func<T> defaultFactory)
{
    return  list.Concat(Enumerable.Range(0, count).Select(i => defaultFactory())).Take(count);
}

次のように呼び出します。

var result = myList.TakeOrDefault(20, () => new Foo())

もちろん、両方の方法を共存させることができるため、次のように簡単に設定できます。

// pad a list of ints with zeroes
var intResult = myIntList.TakeOrDefault(20, default(int));
// pad a list of objects with null
var objNullResult = myObjList.TakeOrDefault(20, (object)null);
// pad a list of Foo with new (separate) instances of Foo
var objPadNewResult = myFooList.TakeOrDefault(20, () => new Foo());
于 2015-07-27T15:00:36.797 に答える
11

デフォルトではそこにはありませんが、拡張メソッドとして書くのは簡単です

public static IEnumerable<T> TakeOrDefault<T>(this IEnumerable<T> items, int count, T defaultValue)
{
    var i = 0;
    foreach(var item in items)
    {
        i++;
        yield return item;
        if(i == count)
             yield break;
    }
    while(i++<count)
    {
        yield return defaultValue;
    }
}

実際の例: http://rextester.com/XANF91263

于 2015-07-27T15:00:11.540 に答える
5

あなたが探しているのは、PadTo必要に応じて特定の値を使用してコレクションの長さを拡張する汎用メソッドです。

public static IEnumerable<T> PadTo<T>(this IEnumerable<T> source, int len)
{
    return source.PadTo(len, default(T));
}

public static IEnumerable<T> PadTo<T>(this IEnumerable<T> source, int len, T elem)
{
    return source.PadTo(len, () => elem);
}

public static IEnumerable<T> PadTo<T>(this IEnumerable<T> source, int len, Func<T> elem)
{
    int i = 0;
    foreach(var t in source)
    {
        i++;
        yield return t;
    }

    while(i++ < len)
        yield return elem();
}

次のように表現できます。

myList.Take(20).PadTo(20);

これは Scala のList[A].padTo

于 2015-07-27T15:09:32.390 に答える
1

Concatこの目的で使用できます。簡単なヘルパー メソッドを使用して、これをすべて結合できます。

public IEnumerable<T> TakeSpawn(this IEnumerable<T> @this, int take, T defaultElement)
{
  return @this.Concat(Enumerable.Repeat(defaultElement, take)).Take(take);
}

アイデアは、常に元の列挙型の末尾に別の列挙型を追加することです。そのため、入力に十分な要素がない場合、Repeat.

于 2015-07-27T14:59:57.667 に答える
0

.NET Framework には、私が認識しているもの以外は何もありません。ただし、これは拡張メソッドを使用して簡単に実現できます。デフォルト値を自分で指定すると、すべてのタイプで機能します。

public static class ListExtensions
{
    public static IEnumerable<T> TakeOrDefault<T>(this List<T> list, int count, T defaultValue)
    {
        int missingItems = count - list.Count;
        List<T> extra = new List<T>(missingItems);

        for (int i = 0; i < missingItems; i++)
            extra.Add(defaultValue);

        return list.Take(count).Concat(extra);
    }
}
于 2015-07-27T15:08:40.967 に答える
0

カウントをチェックし、残りのエントリのデフォルト値を返す拡張メソッドを作成してみませんか?

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication3
{
    class Program
    {
        static void Main(string[] args)
        {
            List<int> values = new List<int>{1, 2, 3, 4};

            IEnumerable<int> moreValues = values.TakeOrDefault(3, 100);
            Console.WriteLine(moreValues.Count());

            moreValues = values.TakeOrDefault(4, 100);
            Console.WriteLine(moreValues.Count());

            moreValues = values.TakeOrDefault(10, 100);
            Console.WriteLine(moreValues.Count());

        }
    }

    public static class ExtensionMethods
    {
        public static IEnumerable<T> TakeOrDefault<T>(this IEnumerable<T> enumerable, int count, T defaultValue)
        {
            int returnedCount = 0;
            foreach (T variable in enumerable)
            {
                returnedCount++;
                yield return variable;
                if (returnedCount == count)
                {
                    yield break;
                }
            }

            if (returnedCount < count)
            {
                for (int i = returnedCount; i < count; i++)
                {
                    yield return defaultValue;
                }
            }
        }
    }
}
于 2015-07-27T15:14:17.607 に答える
0

T が値型であることに依存する、これに対する簡単な拡張機能を作成しました。

  public static class Extensions
    {
        public static IEnumerable<T> TakeOrDefault<T>(this IEnumerable<T> list, int totalElements)
        {
            List<T> finalList = list.ToList();

            if (list.Count() < totalElements)
            {
                for (int i = list.Count(); i < totalElements; i++)
                {
                    finalList.Add(Activator.CreateInstance<T>());
                }
            }

            return finalList;
        }
    }
于 2015-07-27T15:18:01.293 に答える