Co robi metoda „map” w Ruby?

250

Jestem nowy w programowaniu. Czy ktoś może wyjaśnić, co .mapby zrobił w:

params = (0...param_count).map
bigpotato
źródło
9
Zadaj jedno pytanie na raz. mapjest powszechną „funkcjonalną” metodą znajdującą się na obiektach policzalnych używanych do przekształcania wartości w sekwencji (ze szczególnym uwzględnieniem). ..i ...są sposobami tworzenia zakresów. Również zapoznać się z REPL, gdzie można spróbować tej rzeczy z siebie! :)
5
REPL dla rubinu to irb, dla Railsów to c. REPL pozwala testować kod bezpośrednio na samej powłoce językowej.
Gary

Odpowiedzi:

431

mapMetoda bierze przeliczalny obiekt i blok i uruchamia blok dla każdego elementu, wyprowadzanie każdy zwracanej wartości z bloku (oryginalny obiekt pozostaje niezmieniona, chyba że używasz map!):

[1, 2, 3].map { |n| n * n } #=> [1, 4, 9]

Arrayi Rangesą typami wymiennymi. mapz blokiem zwraca tablicę. map!mutuje oryginalną tablicę.

Gdzie to jest pomocne i na czym polega różnica map! i each? Oto przykład:

names = ['danil', 'edmund']

# here we map one array to another, convert each element by some rule
names.map! {|name| name.capitalize } # now names contains ['Danil', 'Edmund']

names.each { |name| puts name + ' is a programmer' } # here we just do something with each element

Wyjście:

Danil is a programmer
Edmund is a programmer
Danil Speransky
źródło
3
dzięki speransky za przykład. to czym różni się .map od .each?
bigpotato
2
Ach, rozumiem. Więc .map faktycznie mutuje tablicę, podczas gdy .each po prostu pętli przez tablicę, aby uzyskać dostęp do wartości, pozostawiając pierwotną tablicę nietkniętą?
bigpotato
24
Dla zwykłych czytelników niebezpieczne jest to, że zdanie wstępne opisuje to maptak, jakby to byłomap!
kalejdycki
12
aby zobaczyć różnicę między mapą a każdą, otwórz okno IRB i spójrz na wyniki dla yi z następującego kodu: y = [1,2,3] .each {| x | x + 1}; z = [1,2,3] .mapa {| x | x + 1}
davej
7
@Inquisitive: „each” zwraca tablicę, która ją wywołuje (w przykładzie [1,2,3]) po dostarczeniu bloku, „map” zwraca nową tablicę wypełnioną wartościami obliczonymi przez blok. Może to pomóc: ustaw zmienną ary = [1,2,3] i sprawdź jej identyfikator_obiektu. Następnie uruchom y = ary.each {| x | x + 1}; z = ary.map {| x | x + 1}. Teraz sprawdź ID_obiektu dla yi z. y ma ten sam identyfikator_obiektu jak aryt (ponieważ każdy zwrócił ary), ale z ma inny identyfikator_obiektu, ponieważ mapa zwróciła nową tablicę.
davej
66

mapwraz z selectieach jest jednym z koni Ruby w moim kodzie.

Pozwala uruchomić operację na każdym obiekcie tablicy i zwrócić je wszystkie w tym samym miejscu. Przykładem może być zwiększenie tablicy liczb o jeden:

[1,2,3].map {|x| x + 1 }
#=> [2,3,4]

Jeśli możesz uruchomić jedną metodę na elementach tablicy, możesz to zrobić w skrócie:

  1. Aby to zrobić w powyższym przykładzie, musisz zrobić coś takiego

    class Numeric
      def plusone
        self + 1
      end
    end
    [1,2,3].map(&:plusone)
    #=> [2,3,4]
  2. Aby po prostu użyć techniki ampersand i skrótu, użyjmy innego przykładu:

    ["vanessa", "david", "thomas"].map(&:upcase)
    #=> ["VANESSA", "DAVID", "THOMAS"]

Przekształcanie danych w Rubim często wiąże się z kaskadą mapoperacji. Studiuj mapi selectsą to jedne z najbardziej przydatnych metod języka Ruby w bibliotece podstawowej. Są tak samo ważne jak each.

( mapjest także pseudonimem collect. Użyj tego, co działa najlepiej dla Ciebie koncepcyjnie).

Więcej przydatnych informacji:

Jeśli obiekt Enumerable, na którym pracujesz eachlub mapna którym znajduje się zawiera zestaw elementów Enumerable (skróty, tablice), możesz zadeklarować każdy z tych elementów w rurach blokowych w następujący sposób:

[["audi", "black", 2008], ["bmw", "red", 2014]].each do |make, color, year|
  puts "make: #{make}, color: #{color}, year: #{year}"
end
# Output:
# make: audi, color: black, year: 2008
# make: bmw, color: red, year: 2014

W przypadku Hash (także Enumerableobiekt, Hash to po prostu tablica krotek ze specjalnymi instrukcjami dla interpretera). Pierwszy „parametr rury” jest kluczem, drugi to wartość.

{:make => "audi", :color => "black", :year => 2008}.each do |k,v|
    puts "#{k} is #{v}"
end
#make is audi
#color is black
#year is 2008

Aby odpowiedzieć na aktualne pytanie:

Zakładając, że paramsjest to skrót, byłby to najlepszy sposób na zmapowanie go: Użyj dwóch parametrów bloku zamiast jednego, aby uchwycić parę klucz i wartość dla każdej interpretowanej krotki w skrócie.

params = {"one" => 1, "two" => 2, "three" => 3}
params.each do |k,v|
  puts "#{k}=#{v}"
end
# one=1
# two=2
# three=3
boulder_ruby
źródło
To nie działa dla mnie w IRB. Dostaję NoMethodError: private method 'plusone' called for 1:Fixnumruby 2 i „złą liczbę argumentów” w ruby ​​1.9 / 1.8. Zresztą, kiedyś lambda: plusone = ->(x) { x + 1 }następnie wyjąć specyfikator symbol: [1,2,3].map(&plusone).
tjmcewan
1
hmm brzmi, jakbyś zadeklarował privatewewnątrz klasy, w której umieściłeś metodę przed jej zastosowaniem
boulder_ruby
Tak, całkowicie się zgadza. Tyle że nie było. :( Po pierwsze było to w prostym skrypcie bez klas, po drugie w zwykłym irb. Oto moja kopia / wklej twojego kodu: gist.github.com/tjmcewan/a7e4feb2976a93a5eef9
tjmcewan
Tak, całkowicie podam zły przykład w moim kodzie, przepraszam. Wypróbuj zmodyfikowany kod. Teraz działa ...
boulder_ruby
1
@ boulder_ruby jest sposób, aby to zrobić za pomocą normalnej metody - jak w metodzie klasowej?
tekknolagi,
6

Używając Ruby 2.4 możesz zrobić to samo, używając transform_valuestej funkcji wyodrębnionej z szyn na ruby.

h = {a: 1, b: 2, c: 3}

h.transform_values { |v| v * 10 }
 #=> {a: 10, b: 20, c: 30}
tokhi
źródło
4

0..param_countoznacza „do param_count włącznie”. 0...param_countoznacza „do, ale bez param_count”.

Range#mapnie zwraca an Enumerable, faktycznie mapuje go na tablicę. To jest tak samo jak Range#to_a.

Pedro Nascimento
źródło
3

„Mapuje” funkcję do każdego elementu w Enumerable- w tym przypadku - zakresie. Wywołałby więc blok przekazany raz dla każdej liczby całkowitej od 0 doparam_count (wyłącznie - masz rację co do kropek) i zwraca tablicę zawierającą każdą zwracaną wartość.

Oto dokumentacja dla Enumerable#map. Posiada również alias collect.

Ry-
źródło
To dziwne, ale w Range#maprzeczywistości konwertuje je na tablicę.
Pedro Nascimento
1
@PedroNascimento: Tak ... tak powiedziałem?
Ry-
Przepraszam, nie wiedziałem, że wywołana mapa sama nie zwraca Enumerable, jak każda z nich. Myślałem, że tak.
Pedro Nascimento,
2

Mapa jest częścią wymiennego modułu. Bardzo podobny do „zbierać” Na przykład:

  Class Car

    attr_accessor :name, :model, :year

    Def initialize (make, model, year)
      @make, @model, @year = make, model, year
    end

  end

  list = []
  list << Car.new("Honda", "Accord", 2016)
  list << Car.new("Toyota", "Camry", 2015)
  list << Car.new("Nissan", "Altima", 2014)

  p list.map {|p| p.model}

Mapa zapewnia wartości iterujące po tablicy, które są zwracane przez parametry bloku.

gkstr1
źródło
mapa jest dokładnie taka sama jak kolekcjonowanie.
BKSpurgeon
0

#each

#eachuruchamia funkcję dla każdego elementu w tablicy. Poniższe dwa fragmenty kodu są równoważne:

x = 10
["zero", "one", "two"].each{|element|
    x++
    puts element
}
x = 10
array = ["zero", "one", "two"]

for i in 0..2
    x++
    puts array[i]
end

#map

#mapstosuje funkcję do każdego elementu tablicy, zwracając wynikową tablicę. Następujące są równoważne:

array = ["zero", "one", "two"]
newArray = array.map{|element| element.capitalize()}
array = ["zero", "one", "two"]

newArray = []
array.each{|element|
    newArray << element.capitalize()
}

#map!

#map!jest podobny #map, ale modyfikuje tablicę na miejscu. Następujące są równoważne:

array = ["zero", "one", "two"]
array.map!{|element| element.capitalize()}
array = ["zero", "one", "two"]
array = array.map{|element| element.capitalize()}
Jivan Pal
źródło