Wyciaganie pierwszego i ostatniego elementu z grupowania

Witam, mam taki problem. Jestem w trakcie robienia wyszukiwarki ulic, potrzebuje zrobic wyswietlanie - pierwszy i ostatni element grupowania.
Zapytanie wyglada obecnie tak:
Street.where("street_name LIKE ?", "%#{street_name}%").group(:street_name, :zone_id)
Wszedzie tam gdzie zmienia sie grupa :zone_id potrzebuje wyciagnac element - znaczy dla kazdej zone_id potrzebuje 1/ostatni element.
Ktos ma pomysł jak to zrobic ?

Na początek, to wydaje mi się, że masz niewłaściwie zbudowane grupowanie.

daje przecież:
ulica:, zone_id:
Akacjowa, 1
Akacjowa, 4
Botaniczna, 2
Botaniczna, 4
Klonowa, 1
Klonowa, 3
Klonowa, 4

Zwróć uwagę, że dane są w kolejności nazw ulic, a nie po zone_id.

Sądzę, że dane musisz grupować po zone_id i nazwie ulicy, a dopiero później prezentować wyniki.
Rozważ, czy nie będzie lepiej najpierw wybrać listę “zon”, a dopiero później, dla każdego elementu tej listy szukać pierwszej i ostatniej ulicy.

A coś takiego?
Nie sprawdzałem, ale chyba powinno działać jeżeli dobrze rozumiem pytanie.

Street.where("street_name LIKE ?", "%#{street_name}%").group_by{|street| [street.street_name, street.zone_id]}.map{|k,v| [v.first, v.last]}

Kurde wyglada ze dziala :wink: Nie wiedzialem ze mozna “mapowac przy grupowaniu”
Jeszcze testuje ale wyglada ze to jest dokladnie to co chcialem zrobic

Nie wiedzialem ze mozna “mapowac przy grupowaniu”

To rozwiazanie różni się znacząco od Twoich prób, bo group_by zamienia kolekcję na tablicę, na której później wykonujesz mapa - wydajnościowo klapa, szukasz czegoś w rodzaju:
Street.select(:street_name, :zone_id).select('MAX(zone_id) AS max_id, MIN(zone_id) AS min_id').group(:street_name)
I wtedy powinieneś dostać rekordy z atrybutami max_id oraz min_id

1 Like

Potrzebuje dokładnie zrobic cos takiego jak napisał BSorbus:

ulica:, zone_id:
Akacjowa, 1
Akacjowa, 4
Botaniczna, 2
Botaniczna, 4
Klonowa, 1
Klonowa, 3
Klonowa, 4

Tylko do tego adresu ulicy dopisac jeszcze jakie numery znajduja sie pod danym adresem czyli:
> Akacjowa 1-50, zone_id:1

Akacjowa 51-57, zone_id:2
Akacjowa 58-100, zone_id:3

Zrobilem to tak:

placeslist = Street.where(“street_name LIKE ?”, “%#{street_name}%”).group(:street_name, :zone_id).limit(50)
placeslist.each do |street|
check_s = Street.where(zone_id: street.zone_id).order(“number DESC”)
first = check_s.first.number
last = check_s.last.number
street_list << {label: street.street_name + " #{first}-#{last} " + (" #" +street.zone.name if street.zone)}
end

Ale juz widze ze wydajnosciowo porazka…

Pojawił sie jeszcze jeden problem jak zmusic baze zeby “last” nie byly wpisy typu: "9A, 2/3/4 " ?

Jeżeli zadbasz o to żeby number był faktycznie numerem i dało się go sortować (fakt że korzystasz z order("number DESC") oznacza że tak pewnie jest) to możesz skorzystać z metod agregujących bazy. Czyli coś typu Street.select("street_name, max(number), min(number)").group(:street_name, :zone_id).

W rezultacie dostaniesz coś typu:

street_name, zone_id, min(number), max(number)
Akacjowa, 1, 1, 50
Akacjowa, 4, 51, 100

Pewnie te numerki możesz sobie jakoś połączyć do stringa w select. Najlepszy sposób jak to zrobić pewnie zależy od tego jakiej bazy używasz.

Baza to mysql, wlasnie zapomnialem ze nr ulicy nie do konca musi byc “stricte numerem” maja literki, ukosniki itp.
Jest jakis sposob na poprawne przesortowanie tego ?

@maykazw na twoim miejscu bym zastanowił się jak to można usystematyzować. Jeżeli potrzebujesz tych literek też to trzymaj to w jednej kolumnie a w innej już znormalizowane dane po których będziesz sobie mógł poprawnie sortować. Inaczej raczej ciężko ci to będzie zrobić.

Generalnie

Street.select('MAX(street_name) as first_address, MIN(street_name) as last_address').where("street_name LIKE ?", "%#{street_name}%").group(:street_name, :zone_id)

Powinno ic dać to co potrzebujesz. Generalnie to co poszukujesz to generalnie “Windowing Functions” możesz też popatrzeć za “Aggregate functions” i popatrzeć na FIRST / LAST funkcjami