How to exchange enumeration elements by exchanging only once?

I am trying to swap the order of individual IEnumerable elements.

Given IEnumerable<int> a; items:

1, 2, 3, 4, 5

and what I want to do is write an exchange iterator, resulting in a.Exchange(1, 2) in:

1, 3, 2, 4, 5

But I do not want the enumeration to be repeated several times for this simple purpose. What I still have:

 public static IEnumerable<T> Exchange<T>( this IEnumerable<T> source, int index1, int index2) { var i=0; foreach(var y in source) { if(index1==i) { var j=0; foreach(var x in source) { if(index2==j) { yield return x; break; } ++j; } } else { if(index2==i) { var j=0; foreach(var x in source) { if(index1==j) { yield return x; break; } ++j; } } else { yield return y; } } ++i; } } 

Here's the assumption that index1 and index2 will not exceed the elements of an enumerable. In most cases, the code did the work of exchanging (ordering), but it is repeated several times. Note index1 and index2 may not be real source indexes, they will be an Mth and Nth element when enumerated.

ToArray or ToList can also increase the iteration time.

0
source share
4 answers

WOLOG assumes index1 less than index2 .

Make your own counter; do not use foreach .

For elements up to index1 , iteration runs fine and each one of them.

Then, when you press index1 , select an array large enough to hold the elements between index1 through index2 - that is, including the index1 th element, but not the index2 th element.

Read the elements in this array using your counter.

Now read the index2 element and produce it.

Now your counter is set to one outside of index2 .

Now enter everything into the array except the index1 th element.

Then print the index1 element.

Then print the rest of the elements normally.

Remember to call Dispose on the enumerator when done.

+8
source

The easiest way is something like this:

 public static IEnumerable<T> Exchange<T>( this IEnumerable<T> source, int index1, int index2) { return source.Select((x, i) => new { x, i }) .OrderBy(p => pi == index1 ? index2 : pi == index2 ? index1 : pi) .Select(p => px); } new[] { 1, 2, 3, 4, 5 }.Exchange(1, 2); // { 1, 3, 2, 4, 5 } 

To do this without OrderBy , I think it would look something like this:

 public static IEnumerable<T> Exchange<T>( this IEnumerable<T> source, int index1, int index2) { if (index1 > index2) { int x = index1; index1 = index2; index2 = x; } int index = 0; List<T> itemsBetweenIndexes = new List<T>(); bool betweenIndexes = false; T temp = default(T); foreach(var item in source) { if (!betweenIndexes) { if (index == index1) { temp = item; betweenIndexes = true; } else { yield return item; } } else { if (index == index2) { betweenIndexes = false; yield return item; foreach(var x in itemsBetweenIndexes) { yield return x; } itemsBetweenIndexes.Clear(); yield return temp; } else { itemsBetweenIndexes.Add(item); } } index++; } } 

This initially happens by searching for an element in index1 giving each element until it finds it. After that, it starts adding items to the internal queue until it finds index2 . At this point, it returns an element in index2 , followed by each element in the queue, in order, followed by the element index1 . He then returns to searching index1 (which he will not find) until he reaches the end of the list.

+2
source

To perform this operation without repeating the original more than once, you will need to save the contents of the subsequence between the indexes that you are changing.

Here's how you can implement the algorithm (I renamed index1 and index2 to smallerIndex and greaterIndex ):

 using (IEnumerator<T> e = source.GetEnumerator()) { IList<T> saved = new List<T>(greaterIndex-smallerIndex+1); int index = 0; while (e.MoveNext()) { // If we're outside the swapped indexes, yield return the current element if (index < smallerIndex || index > greaterIndex) { index++; yield return e.Current; } else if (index == smallerIndex) { var atSmaller = e.Current; // Save all elements starting with the current one into a list; // Continue until you find the last index, or exhaust the sequence. while (index != greaterIndex && e.MoveNext()) { saved.Add(e.Current); index++; } // Make sure we're here because we got to the greaterIndex, // not because we've exhausted the sequence if (index == greaterIndex) { // If we are OK, return the element at greaterIndex yield return e.Current; } // Enumerate the saved items for (int i = 0 ; i < saved.Count-1 ; i++) { yield return saved[i]; } // Finally, return the item at the smallerIndex yield return atSmaller; index++; } } } 

Demo on ideon .

+2
source

You can do this in one pass by creating a List<T> and then returning it as an IEnumerable<T> :

 public static IEnumerable<T> Exchange<T>(this IEnumerable<T> source, int index1, int index2) { // TODO: check index1 and index2 are in bounds of List/Enumerable var result = source.ToList(); // single enumeration // Swap vars var temp = result[index1]; result[index1] = result[index2]; result[index2] = temp; return result; } 
0
source

Source: https://habr.com/ru/post/946458/


All Articles