我有一个包含项目的列表。他们都得到了排序'柱。 sort列的类型为int,它是唯一的。
情景:
排序1;排序2;排序3;
如果用户在列表中向上移动一个项目(例如排序3)(例如到位置1,这将给出排序值1),那么刚刚上升的项目下的项目必须在列表中向下移动,并应相应地应用排序编号。在这种情况下,所有转移的项目排序 - 1。
因此场景的最终状态如下所示:
排序1是排序3;第3类是第2类;排序3现在排序1;
如何使用LINQ
执行此操作?
它不仅仅是3个项目。它可以更多。
[编辑]
public ActionResult Up(int id)
{
var item = dataContext.item.FirstOrDefault(x => x.item == id);
return View(dataContext.items);
}
答案 0 :(得分:4)
这可能不是最容易理解的代码,但我已经测试了它,它似乎按预期工作。
让我们设置一些数据。
var array = new []
{
new { Sort = 1, Value = "foo1", },
new { Sort = 2, Value = "foo2", },
new { Sort = 3, Value = "foo3", },
new { Sort = 4, Value = "foo4", },
};
var oldSort = 1;
var newSort = 3;
首先,根据旧索引和新索引的位置将查询分为三个部分,因此我们可以分别处理每个案例。
var q =
oldSort > newSort ?
array
.Where(x => x.Sort >= newSort && x.Sort < oldSort)
.Select(x => new { Sort = x.Sort + 1, Value = x.Value })
.Union(array.Where(x => x.Sort < newSort || x.Sort > oldSort))
.Union(array.Where(x => x.Sort == oldSort)
.Select(x => new { Sort = newSort, Value = x.Value }))
:
oldSort < newSort ?
array
.Where(x => x.Sort <= newSort && x.Sort > oldSort)
.Select(x => new { Sort = x.Sort - 1, Value = x.Value })
.Union(array.Where(x => x.Sort > newSort || x.Sort < oldSort))
.Union(array.Where(x => x.Sort == oldSort)
.Select(x => new { Sort = newSort, Value = x.Value }))
:
array;
向下移动项目的结果(oldSort = 1
,newSort = 3
):
1 foo2
2 foo3
3 foo1
4 foo4
向上移动项目的结果(oldSort = 4
,newSort = 2
):
1 foo1
2 foo4
3 foo2
4 foo3
更新:查询的工作原理是将序列拆分为三个部分
结果是部件的结合。
更新2 :该查询适用于任意数量的项目,并且故意缺少循环。
更新3 :这是使查询与LINQ-to-Entities一起使用的一种方法。
using (var context = new TestDBEntities())
{
var array = context.TestTables;
var q =
oldSort > newSort ?
array
.Where(x => x.Sort >= newSort && x.Sort < oldSort)
.Select(x => new { Sort = x.Sort + 1, Value = x.Value })
.Union(array.Where(x => x.Sort < newSort || x.Sort > oldSort)
.Select(x => new { Sort = x.Sort, Value = x.Value }))
.Union(array.Where(x => x.Sort == oldSort)
.Select(x => new { Sort = newSort, Value = x.Value }))
:
oldSort < newSort ?
array
.Where(x => x.Sort <= newSort && x.Sort > oldSort)
.Select(x => new { Sort = x.Sort - 1, Value = x.Value })
.Union(array.Where(x => x.Sort > newSort || x.Sort < oldSort)
.Select(x => new { Sort = x.Sort, Value = x.Value }))
.Union(array.Where(x => x.Sort == oldSort)
.Select(x => new { Sort = newSort, Value = x.Value }))
:
array.Select(x => new { Sort = x.Sort, Value = x.Value });
}
不同之处在于类型现在明确兼容。
答案 1 :(得分:2)
我知道你要求LINQ解决方案,但在这种情况下LINQ似乎很复杂,特别是如果你想调整Sort
列。我建议使用for循环和索引的简单旧方法。它就地执行排序操作,但不创建新列表。
为了使其可重用,我将其创建为IList
接口的扩展方法,这使得它也与数组兼容。
为了使其通用,您需要某种方式来访问Sort
列。通过接口公开此列会将解决方案限制为实现此接口的类。因此,我选择了您必须作为代理人传递的访问者。如果Sort
列具有其他名称,例如Order
,它们也可以使用。
public static class ListExtensions
{
public static void MoveItem<T>(this IList<T> list, int fromIndex, int toIndex,
Func<T, int> getSortKey, Action<T, int> setSortKey)
{
T temp = list[fromIndex];
int lastSortKey = getSortKey(temp);
setSortKey(temp, getSortKey(list[toIndex]));
if (fromIndex > toIndex) { // Move towards beginning of list (upwards).
for (int i = fromIndex; i > toIndex; i--) {
list[i] = list[i - 1];
int nextSortKey = getSortKey(list[i]);
setSortKey(list[i], lastSortKey);
lastSortKey = nextSortKey;
}
} else if (fromIndex < toIndex) { // Move towards end of list (downwards).
for (int i = fromIndex; i < toIndex; i++) {
list[i] = list[i + 1];
int nextSortKey = getSortKey(list[i]);
setSortKey(list[i], lastSortKey);
lastSortKey = nextSortKey;
}
}
list[toIndex] = temp;
}
}
您可以使用这样的方法
list.MoveItem(3, 1, x => x.Sort, (x, i) => x.Sort = i);
请注意,您必须传递列表索引而不是排序值。
以下是我用于测试的类。只需在两个测试方法的末尾设置一个断点,以便在locals窗口中检查结果。右键单击Test
类并选择“调用静态方法”,在类视图中开始测试。
public class SomeItem
{
public int Sort { get; set; }
public string Value { get; set; }
public override string ToString()
{
return String.Format("Sort = {0}, Value = {1}", Sort, Value);
}
}
public static class Test
{
public static void MoveUp()
{
List<SomeItem> list = InitializeList();
list.MoveItem(3, 1, x => x.Sort, (x, i) => x.Sort = i);
}
public static void MoveDown()
{
List<SomeItem> list = InitializeList();
list.MoveItem(1, 3, x => x.Sort, (x, i) => x.Sort = i);
}
private static List<SomeItem> InitializeList()
{
return new List<SomeItem> {
new SomeItem{ Sort = 1, Value = "foo1" },
new SomeItem{ Sort = 2, Value = "foo2" },
new SomeItem{ Sort = 3, Value = "foo3" },
new SomeItem{ Sort = 4, Value = "foo4" },
new SomeItem{ Sort = 5, Value = "foo5" }
};
}
}
<强>更新强>
关于调整排序键的注意事项:如果排序键是有序且唯一的,则上述解决方案很有效。如果情况并非总是这样,那么更健壮的解决方案是在将列表存储回DB之前调整排序键,只需将排序键设置为等于列表索引即可。这将简化MoveItem
方法。
public static void MoveItem<T>(this IList<T> list, int fromIndex, int toIndex)
{
T temp = list[fromIndex];
if (fromIndex > toIndex) { // Move towards beginning of list (upwards).
for (int i = fromIndex; i > toIndex; i--) {
list[i] = list[i - 1];
}
} else if (fromIndex < toIndex) { // Move towards end of list (downwards).
for (int i = fromIndex; i < toIndex; i++) {
list[i] = list[i + 1];
}
}
list[toIndex] = temp;
}
public static void FixSortKeys<T>(this IList<T> list, Action<T, int> setSortKey)
{
for (int i = 0; i < list.Count; i++) {
setSortKey(list[i], i);
}
}
答案 2 :(得分:1)
条件运算符在这里很有用:
var newitems = items.Select(x =>
new
{
Value = x.Value,
Sort = x.Sort == oldSort ? newSort :
x.Sort < oldSort && x.Sort >= newSort ? x.Sort + 1 :
x.Sort > oldSort && x.Sort < newSort ? x.Sort - 1 :
x.Sort
});
这是使用Serge's setup:
var items = new []
{
new { Sort = 1, Value = "foo1", },
new { Sort = 2, Value = "foo2", },
new { Sort = 3, Value = "foo3", },
new { Sort = 4, Value = "foo4", },
};
var oldSort = 1;
var newSort = 3;
它的表现很不错(在所有情况下都是O(n)),而且它简洁易读。