web-dev-qa-db-fra.com

Utilisez LINQ pour déplacer un élément en haut de la liste

Existe-t-il un moyen de déplacer un élément de say id = 10 en tant que premier élément d'une liste à l'aide de LINQ?

 Item A - id = 5 
 Item B - id = 10 
 Item C - id = 12 
 Item D - id = 1

Dans ce cas, comment puis-je déplacer avec élégance l’élément C en haut de ma collection List<T>?

C'est le meilleur que j'ai en ce moment:

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();
73
qui

LINQ est puissant pour interroger des collections, créer des projections sur des requêtes existantes ou générer de nouvelles requêtes basées sur des collections existantes Il ne s'agit pas d'un outil permettant de réorganiser les collections existantes en ligne. Pour ce type d'opération, il est préférable d'utiliser le type présent. 

En supposant que vous avez un type avec une définition similaire à celle ci-dessous

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

Alors essayez ce qui suit

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

Que voulez-vous commander, autre que le premier article connu? Si vous ne vous en souciez pas, vous pouvez faire ceci:

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

Fondamentalement, "faux" vient avant "vrai" ...

Certes, je ne sais pas ce que cela fait dans LINQ to SQL, etc.

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

Linq travaille généralement sur Enumerables, il ne faut donc pas que le type sous-jacent soit une collection. Donc, pour déplacer l’article au dessus de la liste, je suggérerais d’utiliser quelque chose comme (si vous avez besoin de préserver l’ordre)

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

Si votre fonction ne renvoie qu'un IEnumerable, vous pouvez d'abord utiliser la méthode ToList() pour le convertir en liste.

Si vous ne conservez pas cet ordre, vous pouvez simplement échanger les valeurs des positions 0 et idx.

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

Cela va insérer l'objet avec id = 12 en haut de la liste et faire pivoter le reste vers le bas, en préservant l'ordre.

27
Nick Gillum

Voici une méthode d'extension que vous voudrez peut-être utiliser. Il déplace les éléments qui correspondent au prédicat donné vers le haut, en préservant l'ordre.

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

Sur le plan de la complexité, je pense que la collection comporterait deux passages, ce qui en ferait O (n), comme dans la version Insertion/Suppression, mais supérieure à la suggestion OrderBy de Jon Skeet.

9
configurator

Vous pouvez "grouper par" en deux groupes avec la clé booléenne, puis les trier

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

C'est intéressant le nombre d'approches que vous trouvez lorsque vous essayez de résoudre un problème 

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

Je sais que c'est une vieille question mais je l'ai fait comme ça

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

ceci imprime:

le nombre est 10 
nombre est 1
nombre est 5
nombre est 12

1
Gaotter
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
Grozz

J'ai écrit une méthode d'extension statique pour faire cela. Notez que cela ne conserve pas l'ordre, il remplace simplement l'élément. Si vous aviez besoin de préserver l'ordre, vous devriez effectuer une rotation et non un simple échange.

/// <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;
}
0
rolls

Pour vérifier également si l'élément a été trouvé sans exception, par exemple:

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
Slai