Tengo un IEnumerable<T>
y un IEnumerable<U>
que quiero fusionar en un IEnumerable<KeyValuePair<T,U>>
donde los índices de los elementos unidos en el KeyValuePair son los mismos. Tenga en cuenta que no estoy usando IList, por lo que no tengo un recuento o un índice para los elementos que estoy fusionando. ¿Cuál es la mejor manera de lograr esto? Preferiría una respuesta LINQ, pero cualquier cosa que haga el trabajo de manera elegante también funcionaría.¿Cómo fusiono (o comprime) dos IEnumerables juntos?
Respuesta
Nota: A partir de .NET 4.0, el marco incluye un método .Zip
extensión de IEnumerable, documentado here. Lo siguiente se mantiene para la posteridad y para su uso en la versión de .NET framework anterior a la 4.0.
que utilizan estos métodos de extensión:
// From http://community.bartdesmet.net/blogs/bart/archive/2008/11/03/c-4-0-feature-focus-part-3-intermezzo-linq-s-new-zip-operator.aspx
public static IEnumerable<TResult> Zip<TFirst, TSecond, TResult>(this IEnumerable<TFirst> first, IEnumerable<TSecond> second, Func<TFirst, TSecond, TResult> func) {
if (first == null)
throw new ArgumentNullException("first");
if (second == null)
throw new ArgumentNullException("second");
if (func == null)
throw new ArgumentNullException("func");
using (var ie1 = first.GetEnumerator())
using (var ie2 = second.GetEnumerator())
while (ie1.MoveNext() && ie2.MoveNext())
yield return func(ie1.Current, ie2.Current);
}
public static IEnumerable<KeyValuePair<T, R>> Zip<T, R>(this IEnumerable<T> first, IEnumerable<R> second) {
return first.Zip(second, (f, s) => new KeyValuePair<T, R>(f, s));
}
EDITAR: después de los comentarios que estoy obligado a aclarar y corregir algunas cosas:
- originalmente tomó la primera aplicación Zip textualmente de Bart De Smet's blog
- Agregado de disposición de enumerador (que también era noted en la publicación original de Bart)
- Añadido comprobación parámetro nulo (también discutido en el post de Bart)
Más bonito que el mío. – erikkallen
Esto es incorrecto ya que supone que IEnumerables conservará el orden. – Welbog
Tipo de: anima al _caller_ a hacer la suposición. Esto hace lo único que puede hacer, y a veces la suposición está bien fundada. –
pensar en lo que estás pidiendo un poco más de cerca aquí:
desea combinar dos IEnumerables en la que "los índices de los elementos unidos entre sí en el KeyValuePair son los mismos", pero que " no tiene un conteo o uníndice para los artículos que estoy fusionando ".
No hay garantía de que sus IEnumerables estén ordenados o no. No existe una correlación entre sus dos objetos IEnumerable, entonces, ¿cómo puede esperar correlacionarlos?
@welbog: Parece que hay un malentendido de la pregunta. Creo que por "índice" Erik significaba la posición del elemento en el IEnumerable (1º, 2º, etc.) –
@mausch: una posición que no está garantizada. Dependiendo de la implementación, el orden de los dos IEnumerables podría no ser el esperado. – Welbog
@welbog: ¿tendría sentido llamar Zip con un enumerable? O no tiene sentido o la persona que llama tiene que ser consciente de esto ... No veo ninguna otra opción. –
no probado, pero debería funcionar:
IEnumerable<KeyValuePair<T, U>> Zip<T, U>(IEnumerable<T> t, IEnumerable<U> u) {
IEnumerator<T> et = t.GetEnumerator();
IEnumerator<U> eu = u.GetEnumerator();
for (;;) {
bool bt = et.MoveNext();
bool bu = eu.MoveNext();
if (bt != bu)
throw new ArgumentException("Different number of elements in t and u");
if (!bt)
break;
yield return new KeyValuePair<T, U>(et.Current, eu.Current);
}
}
Mira nextension:
métodos actualmente implementadas
IEnumerable
- ParaCada Realiza una acción especificada en cada elemento de la interfaz IEnumerable.
- Clump Agrupa los artículos en lotes del mismo tamaño.
- Escaneo Crea una lista aplicando un delegado a pares de elementos en IEnumerable.
- Comprobaciones al menos Hay al menos una cierta cantidad de elementos en el IEnumerable.
- En la mayoría de los cheques no hay más de una cierta cantidad de artículos en el IEnumerable.
- Código postal Crea una lista combinando otras dos listas en una sola.
- Ciclo Crea una lista repitiendo otra lista.
El MSDN tiene la Custom Sequence Operators ejemplo siguiente. Y Welbog tiene razón; si no tiene un índice sobre los datos subyacentes, no tiene garantía de que la operación haga lo que espera.
me gustaría utilizar algo en la línea de -
IEnumerable<KeyValuePair<T,U>> Merge<T,U>(IEnumerable<T> keyCollection, IEnumerable<U> valueCollection)
{
var keys = keyCollection.GetEnumerator();
var values = valueCollection.GetEnumerator();
try
{
keys.Reset();
values.Reset();
while (keys.MoveNext() && values.MoveNext())
{
yield return new KeyValuePair<T,U>(keys.Current,values.Current);
}
}
finally
{
keys.Dispose();
values.Dispose();
}
}
Esto debería funcionar correctamente, y la limpieza adecuada después.
Creo que es bueno llamarlo "zip", ya que es una operación conocida en el mundo funcional. – Daniel
Otra aplicación de la functional-dotnet project por Alexey Romanov:
/// <summary>
/// Takes two sequences and returns a sequence of corresponding pairs.
/// If one sequence is short, excess elements of the longer sequence are discarded.
/// </summary>
/// <typeparam name="T1">The type of the 1.</typeparam>
/// <typeparam name="T2">The type of the 2.</typeparam>
/// <param name="sequence1">The first sequence.</param>
/// <param name="sequence2">The second sequence.</param>
/// <returns></returns>
public static IEnumerable<Tuple<T1, T2>> Zip<T1, T2>(
this IEnumerable<T1> sequence1, IEnumerable<T2> sequence2) {
using (
IEnumerator<T1> enumerator1 = sequence1.GetEnumerator())
using (
IEnumerator<T2> enumerator2 = sequence2.GetEnumerator()) {
while (enumerator1.MoveNext() && enumerator2.MoveNext()) {
yield return
Pair.New(enumerator1.Current, enumerator2.Current);
}
}
//
//zip :: [a] -> [b] -> [(a,b)]
//zip (a:as) (b:bs) = (a,b) : zip as bs
//zip _ _ = []
}
Reemplazar Pair.New
con el nuevo KeyValuePair<T1, T2>
(y el tipo de retorno) y ya está bueno para ir.
como una actualización a cualquiera que tropezarse con esta pregunta, .Net 4.0 soporta de forma nativa como ex desde MS:
int[] numbers = { 1, 2, 3, 4 };
string[] words = { "one", "two", "three" };
var numbersAndWords = numbers.Zip(words, (first, second) => first + " " + second);
- 1. ¿Cómo fusiono dos ejecutables binarios?
- 2. ¿Cómo fusiono dos ramas de SVN?
- 3. ¿Cómo fusiono dos iteradores de python?
- 4. ¿Cómo encadenar dos UIGestureRecognizers juntos?
- 5. C#: comparar el contenido de dos IEnumerables
- 6. Assert IEnumerables
- 7. Conecta dos UIScrollView juntos
- 8. ¿Cómo fusiono dos tablas en Access mientras elimino duplicados?
- 9. ¿Cómo iterar a través de dos IEnumerables simultáneamente?
- 10. ¿Cómo determina Assert.AreEqual la igualdad entre dos IEnumerables genéricos?
- 11. "mover" dos vectores juntos
- 12. ¿Cómo fusiono dos matrices para crear una matriz en JavaScript?
- 13. ¿Cómo fusiono dos tablas sin nombrar todas las columnas?
- 14. ¿Cómo fusiono dos soluciones de Visual Studio diferentes?
- 15. ¿Cómo combino/fusiono columnas de dos resultados de consulta SQL?
- 16. ¿Cómo fusiono una única confirmación?
- 17. ¿Puedo obtener el delta de dos IEnumerables en LINQ?
- 18. Cómo utilizar Zip en tres IEnumerables
- 19. ¿Cómo fusiono diccionarios en Python?
- 20. ¿Cómo fusiono un tenedor principal?
- 21. ¿Cómo fusiono un archivo binario?
- 22. ¿Cómo puedo iterar sobre varios IEnumerables simultáneamente?
- 23. ¿Cómo puedo agregar múltiples IEnumerables de T
- 24. Encadenando IEnumerables en C#?
- 25. esquema XML único Juntos en dos atributos
- 26. ¿Por qué no puedo usar dos o más "browser_action", "page_action" o "app" juntos?
- 27. Mod_gzip comprime las cookies
- 28. Makefile que comprime javascript
- 29. ¿Cómo fusiono los archivos del paquete SSIS?
- 30. ¿Cómo puedo vincular dos objetos serializados de Java juntos?
A partir de .NET 4.0, el marco viene con un Zip [IEnumerable] (http://msdn.microsoft.com/en-us/library/dd267698%28v=vs.110%29.aspx) método de extensión. –
Todavía [otra publicación del blog] (http://blogs.msdn.com/ericlippert/archive/2009/05/07/zip-me-up.aspx) por Eric Lippert – n8wrl
Gracioso - Acabo de leer esto anoche. =) –