Sử dụng LINQ để chuyển mục lên đầu danh sách


80

Có cách nào để di chuyển một mục giả sử id = 10 làm mục đầu tiên trong danh sách bằng LINQ không?

Mặt hàng A - id = 5
Mặt hàng B - id = 10
Mặt hàng C - id = 12
Mặt hàng D - id = 1

Trong trường hợp này, làm cách nào để tôi có thể di chuyển Mục C lên đầu List<T>bộ sưu tập của mình một cách trang nhã ?

Đây là điều tốt nhất tôi có ngay bây giờ:

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

Bạn có muốn hoán đổi mục với mục trên cùng hoặc xoay các mục bằng cách đẩy tất cả các mục cho đến khi mục tìm thấy xuống.
AnthonyWJones

didn; t finalList .insert (0, "nội dung mới"); công việc
Rohit Kumar

Câu trả lời:


52

LINQ mạnh trong việc truy vấn các tập hợp, tạo các phép chiếu trên các truy vấn hiện có hoặc tạo các truy vấn mới dựa trên các tập hợp hiện có. Nó không có nghĩa là một công cụ để sắp xếp lại nội tuyến các bộ sưu tập hiện có. Đối với loại hoạt động đó, tốt nhất là sử dụng loại ở chế độ xử lý.

Giả sử bạn có một loại có định nghĩa tương tự như bên dưới

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

Sau đó, hãy thử những điều sau

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 trình tốt cho kịch bản trao đổi, I Gotta Feelin' rằng một xoay là thực sự cần thiết tho'
AnthonyWJones

Đây là nhiều hơn hoặc ít hơn những gì tôi đã làm mọi cách, nhưng nhờ những lời giải thích là tại sao có vẻ không phải là một cách tốt hơn :)
qui

6
Để xử lý lỗi, lưu ý rằng bạn nên kiểm tra FindIndexgiá trị kết quả, nó là -1 nếu mục không được tìm thấy trong danh sách.
schnaader,

Đây không phải chỉ là hoán đổi phần tử đầu tiên với chỉ mục phần tử mục tiêu thay vì di chuyển phần tử mục tiêu lên trên cùng và di chuyển mọi thứ khác xuống dưới?
frostshoxx

141

Bạn muốn đặt món gì, ngoài món hàng đầu đã biết? Nếu bạn không quan tâm, bạn có thể làm điều này:

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

Về cơ bản, "false" đứng trước "true" ...

Phải thừa nhận rằng tôi không biết điều này làm gì trong LINQ to SQL, v.v. Bạn có thể cần ngăn nó thực hiện thứ tự trong cơ sở dữ liệu:

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

1
nó không hoạt động như mong đợi đối với LINQ to SQL. Tôi chỉ thử nghiệm nó.
Yasser Shaikh

5
+1 Cảm ơn Jon. Tôi muốn đặt hàng theo tên nhưng vẫn giữ mặt hàng có id = 0 ở trên cùng nên tôi đã thực hiện điều này: allCountries.OrderBy (x => x.id == 0? "00000": x.Name) .ToList (); hiệu suất không phải là một vấn đề vì danh sách nhỏ.
nima

3
Đối với những người xem lại mã sau đó, có thể không rõ ràng rằng các giá trị boolean được sắp xếp theo thứ tự "false, true". Tôi muốn giới thiệu các giải pháp chi tiết hơn về vấn đề này.
rymdsmurf

1
Xinh đẹp! Tôi muốn có một Namedanh sách nhỏ ở trên cùng hoặc Chỉ mục 0, trong LINQ cho Thực thể, và nó đã thực hiện được mẹo Cảm ơn +1. db.Systms.Where(s => s.IsActive == true).OrderBy(x => x.SystemName != "Portal Administration").ToList();
Irfan

Công cụ tuyệt vời! Một giải pháp đơn giản tốt đẹp. Cảm ơn,
Hugo Nava Kopp

43

Linq thường hoạt động trên Enumerables, vì vậy không phải bây giờ kiểu cơ bản là một tập hợp. Vì vậy, để di chuyển mặt hàng lên đầu danh sách, tôi khuyên bạn nên sử dụng một cái gì đó như (nếu bạn cần giữ lại đơn đặt hàng)

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

Nếu hàm của bạn chỉ trả về IEnumerable, trước tiên bạn có thể sử dụng ToList()phương pháp này để chuyển nó thành List

Nếu bạn không giữ nguyên thứ tự, bạn có thể chỉ cần hoán đổi các giá trị ở vị trí 0 và vị trí idx


Điều này là hoàn hảo cho kịch bản xoay xuống thay vì chỉ hoán đổi các giá trị.
Bradley Mountford

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

Thao tác này sẽ chèn đối tượng có id = 12 vào đầu danh sách và xoay phần còn lại xuống dưới, giữ nguyên thứ tự.


Tôi thích quá trình suy nghĩ này, nhưng D có ID là 1, vì vậy điều này sẽ không sắp xếp nó là C, D, A, B?
David

3
@PhatWrat Chắc chắn, vì vậy nếu bạn muốn tránh điều đó bạn sẽ nói .ThenBy (o => o.name) hoặc một cái gì đó tương tự
Nick Gillum

Nên là câu trả lời hàng đầu! Cảm ơn
Mantisimo

10

Đây là một phương pháp mở rộng mà bạn có thể muốn sử dụng. Nó di chuyển (các) phần tử phù hợp với vị từ đã cho lên đầu, giữ nguyên thứ tự.

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

Về độ phức tạp, tôi nghĩ rằng nó sẽ thực hiện hai lần trên bộ sưu tập, khiến nó trở thành O (n), giống như phiên bản Chèn / Xóa, nhưng tốt hơn đề xuất OrderBy của Jon Skeet.


2

Bạn có thể "nhóm theo" thành hai nhóm bằng khóa Boolean, rồi sắp xếp chúng

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

2

Tôi biết đây là một câu hỏi cũ nhưng tôi đã làm như thế này

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

bản in này:

số là 10
số là 1
số là 5
số là 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

Điều thú vị là số lượng cách tiếp cận bạn tìm thấy khi cố gắng giải quyết một vấn đề.

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

Để kiểm tra xem mục có được tìm thấy mà không có Ngoại lệ hay không, như sau:

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

Tôi đã viết một phương thức mở rộng tĩnh để thực hiện việc này. Lưu ý rằng điều này không bảo toàn đơn đặt hàng, nó chỉ đơn giản là hoán đổi mặt hàng. Nếu bạn cần để duy trì thứ tự, bạn nên thực hiện một vòng quay chứ không phải một sự hoán đổi đơn giản.

/// <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;
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.