it-roy-ru.com

Как получить случайный объект с помощью Linq

Я пытаюсь получить случайный объект в linq. Вот как я это сделал. 

//get all the answers
var Answers = q.Skip(1).Take(int.MaxValue);
//get the random number by the number of answers
int intRandomAnswer = r.Next(1, Answers.Count());
int count = 0;

//locate the answer
foreach(var Answer in Answers)
{
    if (count == intRandomAnswer)
    {
        SelectedPost = Answer;
        break;
    }
    count++;
}

Это лучший способ сделать это?

26
Luke101

Как насчет:

SelectedPost = q.ElementAt(r.Next(1, Answers.Count()));

Дальнейшее чтение:

Комментарии ниже вносят хороший вклад в тесно связанные вопросы, и я включу их здесь, так как, как указывает @Rouby, люди, которые ищут ответ на них, могут найти этот ответ, и он не будет правильным в этих случаях.

Случайный элемент на всем входе

Чтобы сделать все элементы кандидатами в случайный выбор, вам нужно изменить ввод на r.Next:

SelectedPost = Answers.ElementAt(r.Next(0, Answers.Count()));

@Zidad добавляет полезный метод расширения, чтобы получить случайный элемент для всех элементов в последовательности:

public static T Random<T>(this IEnumerable<T> enumerable)
{
    if (enumerable == null)
    {
         throw new ArgumentNullException(nameof(enumerable));
    }

    // note: creating a Random instance each call may not be correct for you,
    // consider a thread-safe static instance
    var r = new Random();  
    var list = enumerable as IList<T> ?? enumerable.ToList(); 
    return list.Count == 0 ? default(T) : list[r.Next(0, list.Count)];
}
36
codekaizen

Используйте перемешивание Fisher-Yates-Durstenfeld .

(Вы можете использовать вспомогательный метод/метод расширения для перемешивания вашей последовательности IEnumerable<T> . В качестве альтернативы, если вы использовали IList<T>, вы могли бы выполнить перемешивание на месте , если хотите.)

9
LukeH

Еще один дурацкий подход (не самый эффективный для больших наборов данных):

SelectedPost = q.OrderBy(qu => Guid.NewGuid()).First();
8
BFree

Общий метод расширения, основанный на принятом ответе (который не всегда пропускает первый, а перечисляет перечисляемый только один раз):

 public static class EnumerableExtensions
    {
        public static T Random<T>(this IEnumerable<T> enumerable)
        {
            var r = new Random();
            var list = enumerable as IList<T> ?? enumerable.ToList();
            return list.ElementAt(r.Next(0, list.Count()));
        }
    }
4
Wiebe Tijsma
var Rand = new Random();
var SelectedPost = q.Skip(Rand.Next(0,q.Count)).Take(1);

Оптимально, вы хотите, чтобы когда-либо только выполнялся запрос функции для одного значения, поэтому вы устанавливаете Skip/Take, чтобы перейти к порядковому номеру, совпадающему со случайным числом, которое вы генерируете (ограничено itemcount набора данных, поэтому проблема с отсутствующей строкой ограничение на основе MAX (pkey) не является проблемой), а затем выхватывает первый элемент в этой точке последовательности.

В SQL это то же самое, что запросить SELECT Count(*) FROM q, затем SELECT * FROM q LIMIT {0}, 1, где {0} - это Rand.Next(0, count), что должно быть довольно эффективным.

3
Garandy

Поздно на вечеринку, но это высокий результат Google. Краткая версия может быть:

var rnd = new Random();
var SelectedPost = q.OrderBy(x => rnd.Next()).Take(1);

Недостатком является то, что он будет применять случайное число ко всем элементам, но он компактен и может быть легко изменен, чтобы принимать более одного случайного элемента.

2
Dan

Я публикую ответ, потому что у меня недостаточно репутации, чтобы комментировать.

Мне нравится этот ответ:

SelectedPost = q.ElementAt(r.Next(1, Answers.Count()));

Но ElementAt основан на нуле, обязательно начиная с 1 и заканчивая Ответами. Количество () вы в конечном итоге потенциально выбрасываете из диапазона, и вы никогда не получите первую сущность.

Не было бы

SelectedPost = q.ElementAt(r.Next(0, Answers.Count() - 1));

Будь лучше?

1
Tod

Извлечение всех ответов и их зацикливание - не самый эффективный способ, поскольку вы перемещаете много данных из базы данных. Если вы используете целочисленный первичный ключ, который автоматически увеличивается, вы должны получить максимум вашего первичного ключа и затем найти случайное целое число в этом диапазоне. Затем непосредственно получите единственный ответ на основе первичного ключа, полученного из случайной функции.

0
Turnkey

У меня есть таблица товаров в базе данных, каждый раз, когда пользователь вводит одну деталь товара, я хочу показать 10 похожих товаров внизу страницы. И при каждом обновлении этот список должен изменяться. Он должен появляться случайным образом.

Линк выглядит так

var products =
            DataContextFactory.GetDataContext()
                .Set<Product>()
                .Where(x =>x.Id!=id)
                .OrderBy(emp => Guid.NewGuid())
                .Take(10).ToList();

x.Id!=id 

это только для не помещать выбранный продукт в список.

Работает отлично 

0
RASKOLNIKOV