Có cách LINQ nào để hoán đổi vị trí của hai mục bên trong a list<T>
?
Có cách LINQ nào để hoán đổi vị trí của hai mục bên trong a list<T>
?
Câu trả lời:
Kiểm tra câu trả lời từ Marc từ C #: Cách triển khai tốt / tốt nhất của phương pháp Hoán đổi .
public static void Swap<T>(IList<T> list, int indexA, int indexB)
{
T tmp = list[indexA];
list[indexA] = list[indexB];
list[indexB] = tmp;
}
có thể là linq-i-fied như thế nào
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);
Có thể ai đó sẽ nghĩ ra một cách thông minh để làm điều này, nhưng bạn không nên. Việc hoán đổi hai mục trong một danh sách vốn có rất nhiều tác dụng phụ nhưng các hoạt động LINQ sẽ không có tác dụng phụ. Do đó, chỉ cần sử dụng một phương thức mở rộng đơn giản:
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;
}
}
List<T>
có một Reverse()
phương thức, tuy nhiên nó chỉ đảo ngược thứ tự của hai (hoặc nhiều) mục liên tiếp .
your_list.Reverse(index, 2);
Trong đó tham số thứ hai 2
cho biết chúng ta đang đảo ngược thứ tự của 2 mục, bắt đầu với mục ở vị trí đã cho index
.
Nguồn: https://msdn.microsoft.com/en-us/library/hf2ay11y(v=vs.110).aspx
Không có phương thức Hoán đổi hiện tại, vì vậy bạn phải tự tạo một phương thức. Tất nhiên bạn có thể linqify nó, nhưng điều đó phải được thực hiện với một quy tắc (bất thành văn?) Trong tâm trí: Phép toán LINQ không thay đổi các tham số đầu vào!
Trong các câu trả lời "linqify" khác, danh sách (đầu vào) được sửa đổi và trả về, nhưng hành động này ngăn chặn quy tắc đó. Nếu sẽ là kỳ lạ nếu bạn có một danh sách với các mục chưa được sắp xếp, hãy thực hiện thao tác LINQ "OrderBy" và khám phá ra rằng danh sách đầu vào cũng được sắp xếp (giống như kết quả). Điều này không được phép xảy ra!
Vì vậy, làm thế nào để chúng tôi làm điều này?
Suy nghĩ đầu tiên của tôi là khôi phục bộ sưu tập sau khi nó được lặp lại xong. Nhưng đây là một dung dịch bẩn , vì vậy không sử dụng nó:
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;
}
Giải pháp này là bẩn vì nó không sửa đổi danh sách đầu vào, ngay cả khi nó khôi phục nó về tình trạng ban đầu. Điều này có thể gây ra một số vấn đề:
Có một giải pháp tốt hơn (và ngắn hơn): chỉ cần tạo một bản sao của danh sách ban đầu. (Điều này cũng làm cho nó có thể sử dụng IEnumerable làm tham số, thay vì 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;
}
Một nhược điểm của giải pháp này là nó sao chép toàn bộ danh sách sẽ tiêu tốn bộ nhớ và điều đó làm cho giải pháp khá chậm.
Bạn có thể xem xét giải pháp sau:
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];
}
}
Và nếu bạn muốn nhập tham số là 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 cũng tạo một bản sao của (một tập hợp con của) nguồn. Vì vậy, trong trường hợp xấu nhất, nó cũng chậm và ngốn bộ nhớ như hàm Swap2.
Nếu thứ tự quan trọng, bạn nên giữ một thuộc tính trên các đối tượng "T" trong danh sách biểu thị trình tự. Để hoán đổi chúng, chỉ cần hoán đổi giá trị của thuộc tính đó, sau đó sử dụng giá trị đó trong .Sort ( so sánh với thuộc tính chuỗi )