¿Cuál es la mejor manera de obtener exactamente valores x de un Enumerable en C#? Si uso Enumerable .Take() así:
var myList = Enumerable.Range(0,10); var result = myList.Take(20);El resultado solo tendrá 10 elementos.
Quiero llenar las entradas que faltan con un valor predeterminado. Algo como esto:
var myList = Enumerable.Range(0,10); var result = myList.TakeOrDefault(20, default(int)); //Is there anything like this?¿Existe tal función en C # y, de no ser así, cuál sería la mejor manera de lograrlo?
Podrías hacer algo como:
var result = myList.Concat(Enumerable.Repeat(default(int), 20)).Take(20);Y sería fácil convertir esto en un método de extensión:
public static IEnumerable<T> TakeOrDefault<T>(this IEnumerable<T> list, int count, T defaultValue) { return list.Concat(Enumerable.Repeat(defaultValue, count)).Take(count); } Pero hay un problema sutil aquí. Esto funcionaría perfectamente bien para los tipos de valor , para un tipo de referencia, si su defaultValue no es nulo, está agregando el mismo objeto varias veces. Que probablemente no es lo que quieres. Por ejemplo, si tuvieras esto:
var result = myList.TakeOrDefault(20, new Foo()); Vas a agregar la misma instancia de Foo para completar tu colección. Para resolver ese problema, necesitarías algo como esto:
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); }Que llamarías así:
var result = myList.TakeOrDefault(20, () => new Foo())Por supuesto, ambos métodos pueden coexistir, por lo que fácilmente podría tener:
// 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());No está allí de forma predeterminada, pero es bastante fácil escribirlo como un método de extensión.
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; } }Ejemplo en vivo: http://rextester.com/XANF91263
Lo que está buscando es un método PadTo de propósito general, que extiende la longitud de la colección si es necesario usando un valor dado.
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(); }Ahora puede expresar:
myList.Take(20).PadTo(20); Esto es análogo a la List[A].padTo