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

didn; t finalList .insert (0, "neww stuff"); 작업
Rohit Kumar

답변:


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
잘 스왑 시나리오 I '는 회전이 실제로 그래도 필요하다는'느끼고 꼭 +1 작품
AnthonyWJones

이것은 내가 어떤 식 으로든 어느 정도했지만 더 나은 방법이 아닌 이유에 대한 설명에 감사드립니다 :)
qui

6
오류 처리를 위해 FindIndex결과 값을 확인해야 합니다. 목록에 항목이 없으면 -1입니다.
schnaader

대상 요소를 맨 위로 이동하고 다른 모든 항목을 아래로 이동하는 것이 아니라 첫 번째 요소를 대상 요소 인덱스로 교체하는 것이 아닙니까?
frostshoxx

143

알려진 상위 항목 외에 무엇을 주문 하시겠습니까? 신경 쓰지 않는다면 다음과 같이 할 수 있습니다.

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

기본적으로 "false"는 "true"앞에옵니다 ...

LINQ to SQL 등에서 이것이 무엇을하는지 모르겠습니다. 데이터베이스에서 순서를 지정하지 못하도록해야 할 수도 있습니다.

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

1
LINQ to SQL에서 예상대로 작동하지 않습니다. 방금 테스트했습니다.
Yasser Shaikh 2012 년

5
+1 감사합니다 Jon. 이름으로 주문하고 싶지만 맨 위에 id = 0 인 항목을 유지하여 이렇게했습니다. allCountries.OrderBy (x => x.id == 0? "00000": x.Name) .ToList (); 목록이 작기 때문에 성능은 문제가되지 않습니다.
nima

3
나중에 코드를 검토하는 사람에게는 부울 값이 "거짓, 참"으로 정렬되어 있는지 명확하지 않을 수 있습니다. 나는 이것에 대해 더 자세한 솔루션을 권장합니다.
rymdsmurf

1
아름다운! NameLINQ to Entities의 상단 또는 인덱스 0에 작은 목록을 원했고 감사 +1을했습니다. db.Systms.Where(s => s.IsActive == true).OrderBy(x => x.SystemName != "Portal Administration").ToList();
이르판

좋은 물건! 좋은 간단한 솔루션입니다. 감사합니다.
Hugo Nava Kopp 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()메서드를 사용하여 먼저 List로 변환 할 수 있습니다.

순서를 유지하지 않으면 위치 0과 위치 idx의 값을 간단히 바꿀 수 있습니다.


이것은 단순히 값을 바꾸는 대신 아래로 회전 시나리오에 적합합니다.
Bradley Mountford

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

그러면 목록 맨 위에 id = 12 인 개체가 삽입되고 나머지는 순서를 유지하면서 아래로 회전합니다.


나는이 사고 과정을 좋아하지만 D는 1의 ID를 가지고있다. 그래서이 순서는 C, D, A, B가 아닐까?
David

3
@PhatWrat 물론, 그래서 당신은 당신이 (O => o.name) 또는 이와 유사한 .ThenBy 말 것을 방지하기 위해 원하는 경우
닉 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)));
}

복잡성 측면에서 나는 컬렉션에 대해 두 번의 패스를 만들어서 삽입 / 제거 버전처럼 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 ));

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.