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();
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;
既知のトップアイテム以外の注文方法を教えてください。気にしない場合、これを行うことができます:
var query = allCountries.OrderBy(x => x.id != 592).ToList();
基本的に、「false」は「true」の前に来ます...
確かに、これがLINQ to SQLなどで何をするかわかりません。データベースでの順序付けを行わないようにする必要があるかもしれません。
var query = allCountries.AsEnumerable()
.OrderBy(x => x.id != 592)
.ToList();
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の値を単純に交換できます
var allCountries = repository.GetCountries();
allCountries.OrderByDescending(o => o.id == 12).ThenBy(o => o.id)
これにより、リストの先頭にid = 12のオブジェクトが挿入され、残りは順番が保持されて下に回転します。
使用したい拡張メソッドを以下に示します。与えられた述語に一致する要素を、順序を維持しながら上に移動します。
public static IEnumerable<T> MoveToTop(IEnumerable<T> list, Func<T, bool> func) {
return list.Where(func)
.Concat(list.Where(item => !func(item)));
}
複雑さの点では、コレクションに2つのパスを作成し、Insert/RemoveバージョンのようにO(n)にしますが、Jon SkeetのOrderBy提案よりも優れていると思います。
ブールキーを使用して2つのグループに「グループ化」し、並べ替えることができます。
var finalList= allCountries
.GroupBy(x => x.id != 592)
.OrderBy(g => g.Key)
.SelectMany(g => g.OrderBy(x=> x.id ));
興味深いのは、問題を解決しようとするときに見つかるアプローチの数です。
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);
私はこれを古い質問を知っていますが、私はこのようにしました
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です
numberは1です
numberは5です
numberは12
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;
}
これを行うために静的拡張メソッドを作成しました。これは順序を維持せず、単にアイテムを交換するだけです。順序を維持する必要がある場合は、単純なスワップではなく回転を行う必要があります。
/// <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;
}
アイテムが例外なく検出されたかどうかも確認するには、次のようにします。
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();