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

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

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

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}

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.


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.


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.


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()}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.