Sumowanie elementów tablicy

Witam,

Jak zsumować elementy tablicy?

Mam takie zapytanie:

@cls = Client.where('agreement_status_id = ? or agreement_status_id = ?', 2, 6)

Wypisując to w taki sposób:

[code]<% @cls.each do |client| %>
<% allCampLeds = [] %>

<% if client.blank? %>
<% else %>
	<%= allCampLeds << client.leds.count %>
<% end %>

<% end %>[/code]
Dostaję tablicę gdzie mam elementy: 1,1,1,1,1,1,4,3,2,1,1,1,4

Teraz chciałbym je zsumować. Próbowałem już miliona rzeczy i nie mogę tego zrobić.

Proszę o pomoc.

a nie lepiej zrobic Mysqlowego
select count(*) from T where warunek?

Chciałem zrobić tak jak próbuję jednak, bo chcę się nauczyć pracować z tablicami.

A problem jest chyba z tablicą, bo array[0] wyciąga mi wszystko, zamiast jednego rekordu.

  1. Prawidłowo jest to zrobić, tak jak mówi Runge.
  2. Jak już chcesz się bawić, to nie rób tego w widoku! http://www.youtube.com/watch?v=ku3QkWcPSEw półtorej minuty do obejrzenia. Pomijając MVC, to nie ma najmniejszego sensu umieszczać kodu w erb. Czy tak nie jest czytelniej?:

@cls.each do |client| allCampLeds = [] allCampLeds << client.leds.count unless client.blank? end
W ten sposób szybiciej zauwayżysz co robisz źle. Przeanalizuj ten kod.
3) Tak naprawdę interesuje Cię moduł Enumerable: http://ruby-doc.org/core/classes/Enumerable.html

Niestety dalej nie mogę osiągnąć tego. Wszystkie cyfry mam w array[0], a każda cyfra powinna mieć osobny index (arraty[1], array[2] itd.) - wtedy nie byłoby problemu, tak sądzę.

A liczenie tego przez bazę nie daje mi wiarygodnych wyników, bo każdy klient ma różną ilość ekranów.

Oczywiście przeniosę to później do kontrolera, ale na razie robię to w widoku żeby mieć wszystko pod ręką [przechodzę z PHP, a tam można było w kontrolerze testować wszystko ;P].

Jeżeli zrobię tak jak poniżej, to @allCampLeds ma wartość 1.

[code]<% @cls.each do |client| %>
<% @allCampLeds = 0 %>

	<% @allCampLeds += client.leds.count unless client.blank? %> 

<% end %>

<%= @allCampLeds %>

[/code] Dla mnie jest to totalnie nielogiczne, że ten kod nie działa. ;/

Ok, udało się - mój debilny błąd:

[code]<% @allCampLeds = 0 %>
<% @cls.each do |client| %>
<% @allCampLeds += client.leds.count unless client.blank? %>
<% end %>

<%= @allCampLeds %>

[/code] Dzięki za pomoc.

Krócej i czytelniej będzie:

[code=ruby]array = [1,2,3]

array.inject(0) { |sum, value| sum+value } # => 6

lub jeszcz krócej:

array.inject(:+) # => 6[/code]
Edit:
Auć, chyba źle przeczytałem. Zwróciłem uwagę na ten fragment:

W kontrolerze (a najlepiej w modelu), dużo łatwiej byłoby Ci zauważyć ten błąd, ale własnie o to chodziło. :slight_smile:

Cieszy nas, że przechodzisz z PHP, ale ja rozumiem twoje rozumowanie tak:

Zrób przysługę sobie i współpracownikom (przyszłym?), przestań się uderzać młotkiem w głowę już dziś. :slight_smile: Złe nawyki naprawdę trzeba piętnować.

W sensie, że tak?

@cls.collect { |client| client.leds.count }.sum

…bo już się trochę zgubiłem.

  1. Nie używaj camelcase dla nazw zmiennych, to nie jest dobra praktyka, camelcase jest zarezerwowane dla klas.
    Źle:
allCampLeds = []

Dobrze:

total_leds = []
  1. Staraj się używać idiomów Ruby wtedy gdy to możliwe, po twoim kodzie widać że nadal próbujesz się naczyć pisać PHP w kodzie Ruby, powielasz złe praktyki, nie nauczysz się w ten sposób pracować z tablicami w Ruby. Najlepiej zapomnij wogóle o tym jak pisałeś w PHP. Zapoznaj się z http://ruby-doc.org/core/classes/Array.html, http://ruby-doc.org/core/classes/Hash.html a przedewszystkim z http://ruby-doc.org/core/classes/Enumerable.html

Przykład:

total_leds = cls.inject(0) { |c, sum| sum+c.leds.count }
  1. Zacznij wykorzystywać potencjał ActiveRecord, nie rób wszystkiego na piechote. Np jeśli Leds to relacja do Client możesz wykorzystać counter_cache dla tej relacji i ją zsumować. http://guides.rubyonrails.org/association_basics.html#counter_cache

[code]class Led < ActiveRecord::base
belongs_to :client, :counter_cache => true
end

class Client < ActiveRecord::Base
has_many :leds
end[/code]
Dzięki temu nie musisz liczyc już sumy leds’ów per client, a do tego możesz zsumować łatwo wszystko bez agregacji(grupowania) po klientach. Twój kod wygeneruje tyle zapytań count(*) do bazy ilu masz clientów, poniższy przykład wygeneruje tylko 1 zapytanie pobierające klientów i 1 zapytanie sumujące ilośc leds’ów.

@cls = Client.where('agreement_status_id = ? or agreement_status_id = ?', 2, 6) @cls_total_leds = @cls.sum(:leds_count)
4. Postaraj się następnym razem opisać lepije model danych i jego relacje w takich przypadkach żeby być dobrze zrozumianym :slight_smile:

Ok, dzięki za rady, zaraz nad tym posiedzę jeszcze. :slight_smile: