Рейки: виберіть унікальні значення зі стовпця


238

У мене вже є робоче рішення, але мені дуже хочеться знати, чому це не працює:

ratings = Model.select(:rating).uniq
ratings.each { |r| puts r.rating }

Він вибирає, але не друкує унікальні значення, він друкує всі значення, включаючи дублікати. І це в документації: http://guides.rubyonrails.org/active_record_querying.html#selecting-specific-fields


Відповіді:


449
Model.select(:rating)

Результатом цього є колекція Modelпредметів. Не прості оцінки. І з uniqточки зору, вони абсолютно різні. Ви можете скористатися цим:

Model.select(:rating).map(&:rating).uniq

або це (найефективніше)

Model.uniq.pluck(:rating)

# rails 5+
Model.distinct.pluck(:rating)

Оновлення

Мабуть, що стосується рейок 5.0.0.1, він працює лише на запити "верхнього рівня", як вище. Не працює над проксі-серверами колекції (наприклад, "has_many" відносини).

Address.distinct.pluck(:city) # => ['Moscow']
user.addresses.distinct.pluck(:city) # => ['Moscow', 'Moscow', 'Moscow']

У такому випадку слід виконати повторне копіювання після запиту

user.addresses.pluck(:city).uniq # => ['Moscow']

Я зробив: group (: рейтинг) .collect {| r | r.rating} Оскільки map == collection, де я можу прочитати про цей синтаксис, який ви використовували (&: рейтинг)? Я не бачу цього в документації Рубі.
alexandrecosta

@ user1261084: див. символ # to_proc, щоб зрозуміти .map (&: рейтинг). PragDave пояснює
dbenhur

63
Варто зазначити, що Model.uniq.pluck(:rating)це найефективніший спосіб зробити це - це генерує SQL, який використовує, SELECT DISTINCTа не застосовує .uniqдо масиву
Майкі,

23
У Rails 5, Model.uniq.pluck(:rating)будеModel.distinct.pluck(:rating)
нейродинамічний

2
Якщо ви хочете вибрати унікальні значення з відносин has_many, які ви завжди можете зробитиModel.related_records.group(:some_column).pluck(:some_column)
Krzysztof Karski

92

Якщо ви збираєтесь використовувати Model.select, то ви можете просто використовувати DISTINCT, оскільки він поверне лише унікальні значення. Це краще, оскільки це означає, що воно повертає менше рядків і повинно бути трохи швидше, ніж повернути ряд рядків, а потім сказати Rails, щоб вибрати унікальні значення.

Model.select('DISTINCT rating')

Звичайно, це за умови, що ваша база даних розуміє DISTINCTключове слово, і більшість повинна.


6
Model.select("DISTINCT rating").map(&:rating)щоб отримати масив лише оцінок.
Кріс

Відмінно підходить для тих, хто має застарілі програми, які використовують Rails 2.3
Mikey

3
Так ... це працює фантастично - однак це лише повертає атрибут DISTINCT. Як ви можете повернути весь об'єкт Model до тих пір, наскільки він відрізняється? Таким чином, ви матимете доступ до всіх атрибутів у моделі в тих випадках, коли атрибут унікальний.
zero_cool

@Jackson_Sandland Якщо ви хочете об'єкт Model, його потрібно буде встановити з запису в таблиці. Але ви не вибираєте запис лише унікального значення (з того, що може бути декілька записів).
Беніссімо

69

Це теж працює.

Model.pluck("DISTINCT rating")

Я вважаю, що виграш - Ruby 1.9.x і вище. Усі, хто використовує попередню версію, не матимуть її. Якщо ви перебуваєте в 1.9x і вище, рубінові документи говорять, що це також працює: Model.uniq.pluck (: рейтинг)
kakubei

6
pluckце чистий метод Rails> 3.2, який не залежить від Ruby 1.9.x Дивіться apidock.com/rails/v3.2.1/ActiveRecord/Calculations/pluck
Daniel Rikowski


27
Model.uniq.pluck(:rating)

# SELECT DISTINCT "models"."rating" FROM "models"

Це має переваги в тому, що не використовувати рядки sql і не створювати інстанційні моделі


3
Це спричиняє помилку з Rails 5.1 / AR 5.1 => невизначений метод `uniq '
Graham Slick


5
Model.select(:rating).distinct

2
Це єдина офіційно правильна відповідь, яка також є надзвичайно ефективною. Хоча, додавши .pluck(:rating)наприкінці, це зробить саме те, про що вимагала ОП.
Шехаряр

5

Якщо я піду правильно, тоді:

Поточний запит

Model.select(:rating)

повертає масив об'єкта, і ви написали запит

Model.select(:rating).uniq

uniq застосовується на масиві об'єктів, і кожен об'єкт має унікальний ідентифікатор. uniq виконує свою роботу правильно, тому що кожен об'єкт у масиві є uniq.

Існує багато способів вибрати різний рейтинг:

Model.select('distinct rating').map(&:rating)

або

Model.select('distinct rating').collect(&:rating)

або

Model.select(:rating).map(&:rating).uniq

або

Model.select(:name).collect(&:rating).uniq

Ще одне, перший і другий запит: знайти різні дані за SQL-запитом.

Ці запити вважатимуться "london" та "london" однаково, це означає, що він нехтуватиме простором, тому він вибере 'london' один раз у вашому результаті запиту.

Третій і четвертий запит:

знайти дані за допомогою SQL-запиту та для різних даних, застосованих ruby ​​uniq mehtod. ці запити будуть розглядатися як "london" та "london" різні, тому для результатів запиту вони обиратимуть "london" та "london".

будь ласка, віддайте перевагу прикріпленому зображенню для більшого розуміння та ознайомтеся з "Подорожували / чекали RFP"

введіть тут опис зображення


6
map& collectє псевдонімами одного і того ж методу, не потрібно наводити приклади для обох.
Адам Лассек

4

Деякі відповіді не враховують, що ОП хоче масив значень

Інші відповіді не працюють добре, якщо ваша модель має тисячі записів

Однак, гарна відповідь:

    Model.uniq.select(:ratings).map(&:ratings)
    => "SELECT DISTINCT ratings FROM `models` " 

Тому що, спочатку ви генеруєте масив Model (зі зменшеним розміром через вибір), ви витягуєте єдиний атрибут, який мають вибрані моделі (рейтинги)


3

Якщо хтось шукає те саме з Mongoid, тобто

Model.distinct(:rating)

ця зараз не працює, тепер повертає кратні.
EUPHORAY

не повертає виразного
dowi

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.