2

アイテムを含むリストを取得しました。それらはすべて「並べ替え」列を取得しました。ソート列はint型であり、一意です。

シナリオ:

並べ替え1; ソート2; ソート3;

ユーザーがリスト内のアイテムを上に移動した場合(たとえば、並べ替え3)、上に移動したばかりのアイテムの下にあるアイテムを下に移動する必要があります。リストにあり、それに応じてソート番号を適用する必要があります。この場合、シフトされたすべてのアイテムはソート-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);
 }
4

3 に答える 3

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;

まず、クエリは新旧のインデックスの位置に応じて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 

更新:クエリは、シーケンスを3つの部分に分割することで機能します

  • 古いインデックスを持つアイテムは、新しいインデックスを持つアイテムになります。
  • 古いインデックスと新しいインデックスの間のアイテムは、上または下にシフトされます。
  • 残りはインデックスを保持します。

その結果、パーツが結合されます。

更新2:クエリは任意の数のアイテムに対して機能し、ループがないことは意図的なものです。

更新3:クエリをLINQ-to-Entitiesで機能させる1つの方法があります。

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

違いは、タイプが明示的に互換性があることです。

于 2012-11-23T13:31:27.153 に答える
2

SortLINQソリューションを求めていることは承知していますが、この状況でLINQを使用するのは複雑なようです。特に、カラムも調整したい場合はそうです。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);

ソート値ではなく、リストインデックスを渡す必要があることに注意してください。


これが私がテストに使用したクラスです。ローカルウィンドウで結果を調べるには、2つのテストメソッドの最後にブレークポイントを設定するだけです。クラスを右クリックし、[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);
    }
}
于 2012-11-23T14:40:09.607 に答える
2

条件演算子はここで役立ちます:

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))、さらに簡潔で読みやすいです。

于 2012-11-23T15:18:31.047 に答える