簡體   English   中英

C#向上/向下移動項目

[英]C# moving an item up/down

我得到一個包含物品的清單。 他們都有一個'排序'列。 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);
 }

這可能不是最容易理解的代碼,但我已經測試了它,它似乎按預期工作。

讓我們設置一些數據。

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 = 1newSort = 3 ):

1 foo2
2 foo3 
3 foo1 
4 foo4 

向上移動項目的結果( oldSort = 4newSort = 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 });
}

不同之處在於類型現在明確兼容。

我知道您要求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類並選擇“Invoke Static Method”,在Class View中開始測試。

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" }
        };
    }

}

UPDATE

關於調整排序鍵的注意事項:如果排序鍵是有序且唯一的,則上述解決方案很有效。 如果情況並非總是如此,則更可靠的解決方案是在將列表存儲回數據庫之前,通過簡單地將排序關鍵字設置為等於列表索引來調整排序關鍵字。 這將簡化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);
    }
}

條件運算符在這里很有用:

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的設置

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)),而且它簡潔易讀。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM