¿Por qué importa por qué quiere hacer esto? Las personas que buscan en Google "elementos de lista de intercambio c #" querrán una respuesta directa a esta pregunta específica.
Daniel Macias
10
@DanielMacias Esto es tan cierto. Estas respuestas que son como 'pero ¿por qué estás haciendo esto?' son tan molestos. Creo que uno debería proporcionar al menos una respuesta viable antes de intentar discutir los por qué.
julealgon
¿Por qué quieres usar LINQ para hacer esto? si es específico de LINQ, por qué no cambiar el título para agregar LINQ
¿Por qué este método de extensión necesita devolver una lista? Estás modificando la lista en el lugar.
vargonian
12
Entonces puedes encadenar los métodos.
Jan Jongboom
3
¿"Generizado" quizás?
Rhys van der Waerden
5
Advertencia: La versión "LINQified" aún altera la lista original.
Philipp Michalski
32
Quizás alguien piense en una forma inteligente de hacer esto, pero tú no deberías. El intercambio de dos elementos en una lista está intrínsecamente cargado de efectos secundarios, pero las operaciones LINQ deben estar libres de efectos secundarios. Por lo tanto, solo use un método de extensión simple:
Por los efectos secundarios que significa que alteran la lista y, posiblemente, los elementos de la lista - en contraposición a los datos solo consulta de que no se modifica nada
Saret
hace "T temp = list [firstIndex];" crear una copia en profundidad del objeto en la lista?
Paul McCarthy
1
@PaulMcCarthy No, crea un nuevo puntero al objeto original, sin copiar en absoluto.
NetMage
12
List<T>tiene un Reverse()método, sin embargo, solo invierte el orden de dos (o más) elementos consecutivos .
your_list.Reverse(index, 2);
Donde el segundo parámetro 2indica que estamos invirtiendo el orden de 2 elementos, comenzando con el elemento dado index.
Aunque google me trajo aquí, esto es lo que estaba buscando.
Jnr
1
La función .Reverse () no es Linq y no se llama Swap, pero parece que la intención de la pregunta es encontrar una función "incorporada" que realice un intercambio. Esta es la única respuesta que proporciona eso.
Americus
Pero solo es útil para intercambiar elementos adyacentes, por lo que en realidad no es una respuesta.
NetMage
10
No existe un método Swap, por lo que debe crear uno usted mismo. Por supuesto que puede linqificarlo, pero eso debe hacerse con una regla (¿no escrita?) En mente: ¡Las operaciones LINQ no cambian los parámetros de entrada!
En las otras respuestas "linqify", la lista (entrada) se modifica y se devuelve, pero esta acción frena esa regla. Si sería extraño tener una lista con elementos sin clasificar, realice una operación LINQ "OrderBy" y luego descubra que la lista de entrada también está ordenada (como el resultado). ¡Esto no está permitido!
¿Entonces como hacemos esto?
Mi primer pensamiento fue simplemente restaurar la colección después de que terminara de iterar. Pero esta es una solución sucia , así que no la use:
staticpublicIEnumerable<T> Swap1<T>(this IList<T> source, int index1, int index2)
{
// Parameter checking is skipped in this example.// Swap the items.
T temp = source[index1];
source[index1] = source[index2];
source[index2] = temp;
// Return the items in the new order.foreach (T item in source)
yieldreturn item;
// Restore the collection.
source[index2] = source[index1];
source[index1] = temp;
}
Esta solución está sucia porque hace modificar la lista de entrada, incluso si se restaura a su estado original. Esto podría causar varios problemas:
La lista podría ser de solo lectura, lo que arrojará una excepción.
Si la lista es compartida por varios subprocesos, la lista cambiará para los otros subprocesos durante la duración de esta función.
Si ocurre una excepción durante la iteración, la lista no se restaurará. (Esto podría resolverse escribiendo un intento-finalmente dentro de la función Intercambio y poner el código de restauración dentro del bloque finalmente).
Existe una solución mejor (y más breve): simplemente haga una copia de la lista original. (Esto también hace posible usar un IEnumerable como parámetro, en lugar de un IList):
staticpublicIEnumerable<T> Swap2<T>(this IList<T> source, int index1, int index2)
{
// Parameter checking is skipped in this example.// If nothing needs to be swapped, just return the original collection.if (index1 == index2)
return source;
// Make a copy.
List<T> copy = source.ToList();
// Swap the items.
T temp = copy[index1];
copy[index1] = copy[index2];
copy[index2] = temp;
// Return the copy with the swapped items.return copy;
}
Una desventaja de esta solución es que copia la lista completa, lo que consumirá memoria y eso hace que la solución sea bastante lenta.
Podría considerar la siguiente solución:
staticpublicIEnumerable<T> Swap3<T>(this IList<T> source, int index1, int index2)
{
// Parameter checking is skipped in this example.// It is assumed that index1 < index2. Otherwise a check should be build in and both indexes should be swapped.using (IEnumerator<T> e = source.GetEnumerator())
{
// Iterate to the first index.for (int i = 0; i < index1; i++)
yieldreturn source[i];
// Return the item at the second index.yieldreturn source[index2];
if (index1 != index2)
{
// Return the items between the first and second index.for (int i = index1 + 1; i < index2; i++)
yieldreturn source[i];
// Return the item at the first index.yieldreturn source[index1];
}
// Return the remaining items.for (int i = index2 + 1; i < source.Count; i++)
yieldreturn source[i];
}
}
Y si desea ingresar el parámetro para que sea IEnumerable:
staticpublicIEnumerable<T> Swap4<T>(this IEnumerable<T> source, int index1, int index2)
{
// Parameter checking is skipped in this example.// It is assumed that index1 < index2. Otherwise a check should be build in and both indexes should be swapped.using(IEnumerator<T> e = source.GetEnumerator())
{
// Iterate to the first index.for(int i = 0; i < index1; i++)
{
if (!e.MoveNext())
yieldbreak;
yieldreturn e.Current;
}
if (index1 != index2)
{
// Remember the item at the first position.if (!e.MoveNext())
yieldbreak;
T rememberedItem = e.Current;
// Store the items between the first and second index in a temporary list.
List<T> subset = new List<T>(index2 - index1 - 1);
for (int i = index1 + 1; i < index2; i++)
{
if (!e.MoveNext())
break;
subset.Add(e.Current);
}
// Return the item at the second index.if (e.MoveNext())
yieldreturn e.Current;
// Return the items in the subset.foreach (T item in subset)
yieldreturn item;
// Return the first (remembered) item.yieldreturn rememberedItem;
}
// Return the remaining items in the list.while (e.MoveNext())
yieldreturn e.Current;
}
}
Swap4 también hace una copia de (un subconjunto de) la fuente. En el peor de los casos, es tan lento y consume memoria como la función Swap2.
Si el orden es importante, debe mantener una propiedad en los objetos "T" en su lista que denote secuencia. Para intercambiarlos, simplemente intercambie el valor de esa propiedad y luego úselo en el .Sort ( comparación con la propiedad de secuencia )
Eso es si puede aceptar conceptualmente que su T tiene un "orden" inherente, pero no si desea que se clasifique de manera arbitraria sin un "orden" inherente, como en una interfaz de usuario.
Dave Van den Eynde
@DaveVandenEynde, soy un programador bastante novato, así que perdóneme si esta no es una gran pregunta: ¿cuál es el significado de intercambiar elementos en una lista si la situación no implica un concepto de orden?
Mathieu K.
@ MathieuK.bueno, lo que quise decir es que esta respuesta propone que el orden se deriva de una propiedad en los objetos en los que se puede ordenar la secuencia . Ese no suele ser el caso.
Dave Van den Eynde
Estoy entendiendo (quizás mal) que esta respuesta dice: "Numere sus objetos, almacenando los números en una propiedad. Luego, para intercambiar dos elementos, intercambie sus números. Utilice la lista ordenada por esta propiedad".
Respuestas:
Verifique la respuesta de Marc de C #: Buena / mejor implementación del método Swap .
public static void Swap<T>(IList<T> list, int indexA, int indexB) { T tmp = list[indexA]; list[indexA] = list[indexB]; list[indexB] = tmp; }
que puede ser linq-i-fied como
public static IList<T> Swap<T>(this IList<T> list, int indexA, int indexB) { T tmp = list[indexA]; list[indexA] = list[indexB]; list[indexB] = tmp; return list; }
var lst = new List<int>() { 8, 3, 2, 4 }; lst = lst.Swap(1, 2);
fuente
Quizás alguien piense en una forma inteligente de hacer esto, pero tú no deberías. El intercambio de dos elementos en una lista está intrínsecamente cargado de efectos secundarios, pero las operaciones LINQ deben estar libres de efectos secundarios. Por lo tanto, solo use un método de extensión simple:
static class IListExtensions { public static void Swap<T>( this IList<T> list, int firstIndex, int secondIndex ) { Contract.Requires(list != null); Contract.Requires(firstIndex >= 0 && firstIndex < list.Count); Contract.Requires(secondIndex >= 0 && secondIndex < list.Count); if (firstIndex == secondIndex) { return; } T temp = list[firstIndex]; list[firstIndex] = list[secondIndex]; list[secondIndex] = temp; } }
fuente
List<T>
tiene unReverse()
método, sin embargo, solo invierte el orden de dos (o más) elementos consecutivos .your_list.Reverse(index, 2);
Donde el segundo parámetro
2
indica que estamos invirtiendo el orden de 2 elementos, comenzando con el elemento dadoindex
.Fuente: https://msdn.microsoft.com/en-us/library/hf2ay11y(v=vs.110).aspx
fuente
No existe un método Swap, por lo que debe crear uno usted mismo. Por supuesto que puede linqificarlo, pero eso debe hacerse con una regla (¿no escrita?) En mente: ¡Las operaciones LINQ no cambian los parámetros de entrada!
En las otras respuestas "linqify", la lista (entrada) se modifica y se devuelve, pero esta acción frena esa regla. Si sería extraño tener una lista con elementos sin clasificar, realice una operación LINQ "OrderBy" y luego descubra que la lista de entrada también está ordenada (como el resultado). ¡Esto no está permitido!
¿Entonces como hacemos esto?
Mi primer pensamiento fue simplemente restaurar la colección después de que terminara de iterar. Pero esta es una solución sucia , así que no la use:
static public IEnumerable<T> Swap1<T>(this IList<T> source, int index1, int index2) { // Parameter checking is skipped in this example. // Swap the items. T temp = source[index1]; source[index1] = source[index2]; source[index2] = temp; // Return the items in the new order. foreach (T item in source) yield return item; // Restore the collection. source[index2] = source[index1]; source[index1] = temp; }
Esta solución está sucia porque hace modificar la lista de entrada, incluso si se restaura a su estado original. Esto podría causar varios problemas:
Existe una solución mejor (y más breve): simplemente haga una copia de la lista original. (Esto también hace posible usar un IEnumerable como parámetro, en lugar de un IList):
static public IEnumerable<T> Swap2<T>(this IList<T> source, int index1, int index2) { // Parameter checking is skipped in this example. // If nothing needs to be swapped, just return the original collection. if (index1 == index2) return source; // Make a copy. List<T> copy = source.ToList(); // Swap the items. T temp = copy[index1]; copy[index1] = copy[index2]; copy[index2] = temp; // Return the copy with the swapped items. return copy; }
Una desventaja de esta solución es que copia la lista completa, lo que consumirá memoria y eso hace que la solución sea bastante lenta.
Podría considerar la siguiente solución:
static public IEnumerable<T> Swap3<T>(this IList<T> source, int index1, int index2) { // Parameter checking is skipped in this example. // It is assumed that index1 < index2. Otherwise a check should be build in and both indexes should be swapped. using (IEnumerator<T> e = source.GetEnumerator()) { // Iterate to the first index. for (int i = 0; i < index1; i++) yield return source[i]; // Return the item at the second index. yield return source[index2]; if (index1 != index2) { // Return the items between the first and second index. for (int i = index1 + 1; i < index2; i++) yield return source[i]; // Return the item at the first index. yield return source[index1]; } // Return the remaining items. for (int i = index2 + 1; i < source.Count; i++) yield return source[i]; } }
Y si desea ingresar el parámetro para que sea IEnumerable:
static public IEnumerable<T> Swap4<T>(this IEnumerable<T> source, int index1, int index2) { // Parameter checking is skipped in this example. // It is assumed that index1 < index2. Otherwise a check should be build in and both indexes should be swapped. using(IEnumerator<T> e = source.GetEnumerator()) { // Iterate to the first index. for(int i = 0; i < index1; i++) { if (!e.MoveNext()) yield break; yield return e.Current; } if (index1 != index2) { // Remember the item at the first position. if (!e.MoveNext()) yield break; T rememberedItem = e.Current; // Store the items between the first and second index in a temporary list. List<T> subset = new List<T>(index2 - index1 - 1); for (int i = index1 + 1; i < index2; i++) { if (!e.MoveNext()) break; subset.Add(e.Current); } // Return the item at the second index. if (e.MoveNext()) yield return e.Current; // Return the items in the subset. foreach (T item in subset) yield return item; // Return the first (remembered) item. yield return rememberedItem; } // Return the remaining items in the list. while (e.MoveNext()) yield return e.Current; } }
Swap4 también hace una copia de (un subconjunto de) la fuente. En el peor de los casos, es tan lento y consume memoria como la función Swap2.
fuente
Si el orden es importante, debe mantener una propiedad en los objetos "T" en su lista que denote secuencia. Para intercambiarlos, simplemente intercambie el valor de esa propiedad y luego úselo en el .Sort ( comparación con la propiedad de secuencia )
fuente