2010-09-16 31 views
28

Tôi có một List, MyStuff có một thuộc tính của Type Float.LINQ để có được giá trị gần nhất?

Có các đối tượng có giá trị thuộc tính là 10,20,22,30.

Tôi cần viết truy vấn tìm thấy các đối tượng gần bằng 21 nhất, trong trường hợp này, nó sẽ tìm đối tượng 20 và 22. Sau đó, tôi cần phải viết một trong đó tìm thấy đối tượng đóng cửa đến 21 mà không đi qua, và nó sẽ trả về đối tượng với giá trị 20.

Tôi không biết bắt đầu từ đâu. Cứu giúp?

Cảm ơn.

Cập nhật - wow có rất nhiều phản hồi tuyệt vời tại đây. Cảm ơn! Tôi không biết nên làm theo cái gì nên tôi sẽ thử tất cả. Một điều có thể làm cho điều này thú vị hơn (hoặc ít hơn) là cùng một truy vấn sẽ phải áp dụng cho các thực thể LINQ-to-SQL, vì vậy có thể câu trả lời được thu thập từ các diễn đàn MS Linq sẽ hoạt động tốt nhất? Không biết.

+0

Er, 22 là trên 21 .... chắc chắn nó sẽ tìm thấy 20? – cjk

+0

Vâng tôi có nghĩa là 20, xin lỗi về các screwup. – Snowy

Trả lời

18

Dưới đây là một giải pháp đáp ứng các truy vấn thứ hai trong thời gian tuyến tính:

var pivot = 21f; 
var closestBelow = pivot - numbers.Where(n => n <= pivot) 
            .Min(n => pivot - n); 

(Edited từ 'ở trên' thành 'bên dưới' sau khi làm rõ)

Đối với các truy vấn đầu tiên, nó sẽ là đơn giản nhất để sử dụng MoreLinq 's MinBy mở rộng:

var closest = numbers.MinBy(n => Math.Abs(pivot - n)); 

Nó cũng có thể làm nó trong LINQ tiêu chuẩn trong thời gian tuyến tính, nhưng với 2 đường chuyền của nguồn:

var minDistance = numbers.Min(n => Math.Abs(pivot - n)); 
var closest = numbers.First(n => Math.Abs(pivot - n) == minDistance); 

Nếu hiệu quả không phải là một vấn đề, bạn có thể sắp xếp trình tự và chọn giá trị đầu tiên trong O(n * log n) như ot cô ấy đã đăng.

+0

làm thế nào về nếu chúng ta cần 22 thay thế? – deadManN

21

Hãy thử sắp xếp chúng bằng giá trị tuyệt đối của phần chênh lệch giữa số lượng và 21 và sau đó lấy mục đầu tiên:

float closest = MyStuff 
    .Select (n => new { n, distance = Math.Abs (n - 21) }) 
    .OrderBy (p => p.distance) 
    .First().n; 

Hoặc rút ngắn nó theo bình luận @Yuriy Faktorovich của:

float closest = MyStuff 
    .OrderBy(n => Math.Abs(n - 21)) 
    .First(); 
+5

Bạn có thể rút ngắn điều đó bằng cách xóa 'Select' và đặt khoảng cách vào' OrderBy' –

6

Dựa trên this post tại diễn đàn Microsoft Linq:

var numbers = new List<float> { 10f, 20f, 22f, 30f }; 
var target = 21f; 

//gets single number which is closest 
var closest = numbers.Select(n => new { n, distance = Math.Abs(n - target) }) 
    .OrderBy(p => p.distance) 
    .First().n; 

//get two closest 
var take = 2; 
var closests = numbers.Select(n => new { n, distance = Math.Abs(n - target) }) 
    .OrderBy(p => p.distance) 
    .Select(p => p.n) 
    .Take(take);  

//gets any that are within x of target 
var within = 1; 
var withins = numbers.Select(n => new { n, distance = Math.Abs(n - target) }) 
    .Where(p => p.distance <= within) 
    .Select(p => p.n); 
2
List<float> numbers = new List<float>() { 10f, 20f, 22f, 30f }; 
float pivot = 21f; 
var result = numbers.Where(x => x >= pivot).OrderBy(x => x).FirstOrDefault(); 

HOẶC

var result = (from n in numbers 
       where n>=pivot 
       orderby n 
       select n).FirstOrDefault(); 

và ở đây có một phương pháp khuyến nông:

public static T Closest<T,TKey>(this IEnumerable<T> source, Func<T, TKey> keySelector, TKey pivot) where TKey : IComparable<TKey> 
{ 
    return source.Where(x => pivot.CompareTo(keySelector(x)) <= 0).OrderBy(keySelector).FirstOrDefault(); 
} 

Cách sử dụng:

var result = numbers.Closest(n => n, pivot); 
+1

Bạn nên đặt 'OrderBy' * sau * the' Where' để nó không phải sắp xếp nhiều phần tử. – Gabe

+0

@Gabe - Cảm ơn bạn đã đề xuất. Tôi đã sửa đổi mã. –

Các vấn đề liên quan