LINQを使用して、アイテムをリストの一番上に移動します


81

LINQを使用して、たとえばid = 10のアイテムをリストの最初のアイテムとして移動する方法はありますか?

アイテムA-id = 5
アイテムB-id = 10
アイテムC-id = 12
アイテムD-id = 1

この場合、どうすればアイテムCをList<T>コレクションの一番上にエレガントに移動できますか?

これは私が今持っている最高のものです:

var allCountries = repository.GetCountries();
var topitem = allCountries.Single(x => x.id == 592);  
var finalList = new List<Country>();
finalList.Add(topitem);
finalList = finalList.Concat(allCountries.Where(x=> x.id != 592)).ToList();

アイテムを一番上のアイテムと交換するか、見つかったアイテムが下に来るまですべてのアイテムを押してアイテムを回転させますか。
AnthonyWJones 2009年

なかった; t finalList .insert(0、 "neww stuff"); 仕事
RohitKumar19年

回答:


52

LINQは、コレクションのクエリ、既存のクエリに対するプロジェクションの作成、または既存のコレクションに基づく新しいクエリの生成に優れています。これは、既存のコレクションをインラインで並べ替えるツールとして意図されたものではありません。そのタイプの操作には、手元のタイプを使用するのが最善です。

以下と同様の定義を持つタイプがあると仮定します

class Item {
  public int Id { get; set; }
  ..
}

次に、次のことを試してください

List<Item> list = GetTheList();
var index = list.FindIndex(x => x.Id == 12);
var item = list[index];
list[index] = list[0];
list[0] = item;

4
スワップシナリオについても1つの作品、Iなきゃフィーリン回転が実際にカントー必要であること」
AnthonyWJones

これは多かれ少なかれ私がやったことですが、なぜもっと良い方法がないように見えるのかについての説明に感謝します:)
qui

6
エラー処理については、FindIndex結果値を確認する必要があることに注意してください。項目がリストに見つからない場合は-1です。
schnaader 2015

これは、ターゲット要素を上に移動して他のすべてを下に移動するのではなく、最初の要素をターゲット要素のインデックスと交換するだけではありませんか?
frostshoxx

143

既知のトップアイテム以外に、何で注文しますか?気にしない場合は、これを行うことができます:

var query = allCountries.OrderBy(x => x.id != 592).ToList();

基本的に、「false」は「true」の前にあります。

確かに、これがLINQ toSQLなどで何をするのかわかりません。データベースでの順序付けを停止する必要がある場合があります。

var query = allCountries.AsEnumerable()
                        .OrderBy(x => x.id != 592)
                        .ToList();

1
LINQ toSQLでは期待どおりに機能しません。私はそれをテストしました。
Yasser Shaikh 2012

5
+1ありがとうジョン。名前で注文したかったのですが、id = 0のアイテムを一番上に置いたままにして、次のようにしました。allCountries.OrderBy(x => x.id == 0? "00000":x.Name).ToList(); リストが小さいため、パフォーマンスは問題になりません。
nima 2013年

3
後でコードを確認する人にとって、ブール値が「false、true」の順序になっていることは明らかではないかもしれません。これよりも詳細な解決策をお勧めします。
rymdsmurf 2015年

1
綺麗な!NameLINQ to Entitiesの上部の小さなリストまたはインデックス0にを入れたかったのですが、Thanks + 1というトリックを実行しました。 db.Systms.Where(s => s.IsActive == true).OrderBy(x => x.SystemName != "Portal Administration").ToList();
irfan 2016

素晴らしいもの!素晴らしくシンプルなソリューション。ありがとう、
ヒューゴナヴァコップ2016年

43

Linqは通常、Enumerablesで機能するため、基になる型がコレクションであるとは限りません。したがって、リストの一番上にアイテムを移動するには、次のようなものを使用することをお勧めします(順序を保持する必要がある場合)

var idx = myList.FindIndex(x => x.id == 592);
var item = myList[idx];
myList.RemoveAt(idx);
myList.Insert(0, item);

関数がIEnumerableのみを返す場合は、ToList()メソッドを使用して最初にリストに変換できます

順序を保持しない場合は、位置0と位置idxの値を交換するだけです。


これは、値を交換するだけでなく、下に回転するシナリオに最適です。
ブラッドリーマウントフォード2010

36
var allCountries = repository.GetCountries();
allCountries.OrderByDescending(o => o.id == 12).ThenBy(o => o.id) 

これにより、id = 12のオブジェクトがリストの一番上に挿入され、残りが下に回転して順序が維持されます。


私はこの思考プロセスが大好きですが、DのIDは1なので、C、D、A、Bのように並べ替えませんか?
デビッド

3
@PhatWrat確かに、それを避けたい場合は、.ThenBy(o => o.name)または同様のことを言うでしょう
Nick Gillum

1
一番の答えになるはずです!おかげで
Mantisimo

10

使用する可能性のある拡張メソッドは次のとおりです。指定された述語に一致する要素を最上位に移動し、順序を維持します。

public static IEnumerable<T> MoveToTop(IEnumerable<T> list, Func<T, bool> func) {
    return list.Where(func)
               .Concat(list.Where(item => !func(item)));
}

複雑さの点では、コレクションを2回パスして、挿入/削除バージョンのようにO(n)にしますが、JonSkeetのOrderBy提案よりも優れていると思います。


2

ブールキーを使用して2つのグループに「グループ化」し、それらを並べ替えることができます

var finalList= allCountries
                .GroupBy(x => x.id != 592)
                .OrderBy(g => g.Key)
                .SelectMany(g => g.OrderBy(x=> x.id ));

2

私はこれが古い質問であることを知っていますが、私はこのようにそれをしました

class Program
{
    static void Main(string[] args)
    {
        var numbers = new int[] { 5, 10, 12, 1 };

        var ordered = numbers.OrderBy(num => num != 10 ? num : -1);

        foreach (var num in ordered)
        {
            Console.WriteLine("number is {0}", num);
        }

        Console.ReadLine();
    }
}

これは印刷します:

数は10
数は1
数は5
数は12


1
public static IEnumerable<T> ServeFirst<T>(this IEnumerable<T> source, 
    Predicate<T> p)
{
    var list = new List<T>();

    foreach (var s in source)
    {
        if (p(s))
            yield return s;
        else
            list.Add(s);
    }

    foreach (var s in list)
        yield return s;
}

1

問題を解決しようとするときに見つけるアプローチの数は興味深いものです。

var service = AutogateProcessorService.GetInstance();
var allConfigs = service.GetAll();
allConfigs = allConfigs.OrderBy(c => c.ThreadDescription).ToList();
var systemQueue = allConfigs.First(c => c.AcquirerId == 0);
allConfigs.Remove(systemQueue);
allConfigs.Insert(0, systemQueue);

1

アイテムが例外なく見つかったかどうかも確認するには、次のようにします。

var allCountries = repository.GetCountries();
var lookup = allCountries.ToLookup(x => x.id == 592);  
var finalList = lookup[true].Concat(lookup[false]).ToList();
if ( lookup[true].Count() != 1 ) YouAreInTrouble();

0

これを行うために静的拡張メソッドを作成しました。これは順序を保持しないことに注意してください。単にアイテムを交換するだけです。順序を維持する必要がある場合は、単純なスワップではなくローテーションを実行する必要があります。

/// <summary>
/// Moves the item to the front of the list if it exists, if it does not it returns false
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="collection"></param>
/// <param name="predicate"></param>
/// <returns></returns>
public static bool MoveToFrontOfListWhere<T>(this List<T> collection, Func<T, bool> predicate)
{
    if (collection == null || collection.Count <= 0) return false;

    int index = -1;
    for (int i = 0; i < collection.Count; i++)
    {
        T element = collection.ElementAt(i);
        if (!predicate(element)) continue;
        index = i;
        break;
    }

    if (index == -1) return false;

    T item = collection[index];
    collection[index] = collection[0];
    collection[0] = item;
    return true;
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.