Como posso excluir um elemento de uma matriz por valor


343

Eu tenho uma matriz de elementos em Ruby

[2,4,6,3,8]

Preciso remover elementos com valor, 3por exemplo

Como faço isso?


Eu me pergunto por que delete array.delete(3)não funciona dentro de ruby on rails controlador
ImranNaqvi

2
pode ser devido ao active recordmétododelete
ImranNaqvi

Respostas:


481

Eu acho que descobri:

a = [3, 2, 4, 6, 3, 8]
a.delete(3)
#=> 3
a
#=> [2, 4, 6, 8]

189
Eu, pessoalmente, como [1, 2, 3, 4, 5] - [3]o que resulta em => [1, 2, 4, 5]partir irb.
Travis

24
E se houver várias entradas de 3 e quisermos excluir apenas uma delas? (Isto está relacionado tão perguntando isso aqui poderia ser melhor)
Navneet

113
Apenas um aviso de que .delete () retornará o valor excluído, não a matriz modificada com o valor removido.
Joshua Pinter

23
A outra consequência a considerar é que deletemodifica a matriz subjacente enquanto -cria uma nova matriz (que é retornada a você) sem o valor excluído. Dependendo do seu caso de uso, qualquer uma das abordagens pode fazer sentido.
Srt32

2
@ user3721428, delete (3) não se refere ao elemento na posição 3, mas exclui qualquer elemento correspondente ao número inteiro 3. Ele removerá todas as ocorrências de 3 e não tem nada a ver com o índice ou a posição das matrizes.
bkunzi01

226

Tomando emprestado de Travis nos comentários, esta é uma resposta melhor:

Eu pessoalmente gosto do [1, 2, 7, 4, 5] - [7]que resulta => [1, 2, 4, 5]deirb

Modifiquei sua resposta, vendo que 3 era o terceiro elemento em sua matriz de exemplo. Isso pode levar a alguma confusão para aqueles que não percebem que 3 está na posição 2 da matriz.


21
Como srt32 aponta na resposta, há uma distinção importante a ser feita entre usar .deletee -. .deleteretornará o valor que foi removido da matriz, se houver; -não vou. Então [ 1, 2, 3 ] - [ 2 ]retornará [ 1, 3 ], enquanto [ 1, 2, 3 ].delete( 2 )retornará 2.
Argus9

5
array - subArraynão funcionará para Matriz de matrizes , mas array.delete(subArray)funcionará.
Sachin

20
A diferença muito importante entre [1,2,3] - [2]e [1,2,3].delete(2)é que o deletemétodo modifica a matriz original , enquanto [1,2,3] - [3]cria uma nova matriz .
Timothy Kovalev

Re subarrays (comentário de @ Sachin acima) "Claro que sim", você só precisa acertar a notação: [1,2,[2],2,3,4] - [2][1, [2], 3, 4], mas [1,2,[2],2,3,4] - [[2]][1, 2, 2, 3, 4]. :-)
Tom Hundt

69

Outra opção:

a = [2,4,6,3,8]

a -= [3]

o que resulta em

=> [2, 4, 6, 8] 

50

Não tenho certeza se alguém afirmou isso, mas Array.delete () e - = value excluirão todas as instâncias do valor passado a ele dentro da matriz. Para excluir a primeira instância do elemento específico, você pode fazer algo como

arr = [1,3,2,44,5]
arr.delete_at(arr.index(44))

#=> [1,3,2,5]

Poderia haver uma maneira mais simples. Não estou dizendo que essa é uma prática recomendada, mas é algo que deve ser reconhecido.


11
Eu estava procurando uma maneira de fazer isso e excluir apenas uma instância do elemento no caso de duplicatas e isso funciona muito bem!
Xeroshogun 10/10

Eu acho que esta resposta está errada, simplesmente porque arr.index () poderia irnil
windmaomao 15/01

32

Supondo que você deseja excluir 3 por valor em vários locais em uma matriz, acho que a maneira ruby ​​de fazer essa tarefa seria usar o método delete_if:

[2,4,6,3,8,3].delete_if {|x| x == 3 } 

Você também pode usar delete_if na remoção de elementos no cenário de 'matriz de matrizes'.

Espero que isso resolva sua consulta


25

eu gosto de -=[4] maneira mencionada em outras respostas para excluir os elementos cujo valor é 4.

Mas existe esta maneira:

irb(main):419:0> [2,4,6,3,8,6].delete_if{|i|i==6}
=> [2, 4, 3, 8]
irb(main):420:0>

mencionado em algum lugar em " Operações básicas de matriz ", depois de mencionar a mapfunção


Mas você não pode apenas usar.delete(6)
Zac

@Zac, claro, mas essa resposta já foi mencionado (como tem a muito concisa -=maneira a-=[4]ie a=a-[4]. [3,4]-[4], Que eu disse que gostava), mas eu queria mencionar uma outra maneira possível.
Barlop #

Este método também tem a vantagem de retornar a matriz em vez do elemento excluído.
9788 F.Webber

22

Você pode simplesmente executar:

[2,4,6,3,8].delete(3)


15

Aqui estão alguns benchmarks:

require 'fruity'


class Array          
  def rodrigo_except(*values)
    self - values
  end    

  def niels_except value
    value = value.kind_of?(Array) ? value : [value]
    self - value
  end
end

ARY = [2,4,6,3,8]

compare do
  soziev  { a = ARY.dup; a.delete(3);               a }
  steve   { a = ARY.dup; a -= [3];                  a }
  barlop  { a = ARY.dup; a.delete_if{ |i| i == 3 }; a }
  rodrigo { a = ARY.dup; a.rodrigo_except(3);         }
  niels   { a = ARY.dup; a.niels_except(3);           }
end

# >> Running each test 4096 times. Test will take about 2 seconds.
# >> soziev is similar to barlop
# >> barlop is faster than steve by 2x ± 1.0
# >> steve is faster than rodrigo by 4x ± 1.0
# >> rodrigo is similar to niels

E novamente com uma matriz maior contendo muitas duplicatas:

class Array          
  def rodrigo_except(*values)
    self - values
  end    

  def niels_except value
    value = value.kind_of?(Array) ? value : [value]
    self - value
  end
end

ARY = [2,4,6,3,8] * 1000

compare do
  soziev  { a = ARY.dup; a.delete(3);               a }
  steve   { a = ARY.dup; a -= [3];                  a }
  barlop  { a = ARY.dup; a.delete_if{ |i| i == 3 }; a }
  rodrigo { a = ARY.dup; a.rodrigo_except(3);         }
  niels   { a = ARY.dup; a.niels_except(3);           }
end

# >> Running each test 16 times. Test will take about 1 second.
# >> steve is faster than soziev by 30.000000000000004% ± 10.0%
# >> soziev is faster than barlop by 50.0% ± 10.0%
# >> barlop is faster than rodrigo by 3x ± 0.1
# >> rodrigo is similar to niels

E ainda maior com mais duplicatas:

class Array          
  def rodrigo_except(*values)
    self - values
  end    

  def niels_except value
    value = value.kind_of?(Array) ? value : [value]
    self - value
  end
end

ARY = [2,4,6,3,8] * 100_000

compare do
  soziev  { a = ARY.dup; a.delete(3);               a }
  steve   { a = ARY.dup; a -= [3];                  a }
  barlop  { a = ARY.dup; a.delete_if{ |i| i == 3 }; a }
  rodrigo { a = ARY.dup; a.rodrigo_except(3);         }
  niels   { a = ARY.dup; a.niels_except(3);           }
end

# >> Running each test once. Test will take about 6 seconds.
# >> steve is similar to soziev
# >> soziev is faster than barlop by 2x ± 0.1
# >> barlop is faster than niels by 3x ± 1.0
# >> niels is similar to rodrigo

7
Então, qual é o melhor? :)
Kirby

8

Melhorei a solução da Niels

class Array          
  def except(*values)
    self - values
  end    
end

Agora você pode usar

[1, 2, 3, 4].except(3, 4) # return [1, 2]
[1, 2, 3, 4].except(4)    # return [1, 2, 3]

Sua solução não funciona no irbconsola 2.2.1 :007 > [1, 2, 3, 4].except(3, 4) NoMethodError: undefined method except for [1, 2, 3, 4]:Array from (irb):7 from /usr/share/rvm/rubies/ruby-2.2.1/bin/irb:11:in <main>
hgsongra

11
Para declarar no IRB, você precisa adicionar o método à Matriz class Array; def except(*values); self - values; end; end.
Mark Swardstrom

3

Você também pode consertar o macaco. Eu nunca entendi por que Ruby tem um exceptmétodo para, Hashmas não para Array:

class Array
  def except value
    value = value.kind_of(Array) ? value : [value]
    self - value
  end
end

Agora você pode fazer:

[1,3,7,"436",354,nil].except(354) #=> [1,3,7,"436",nil]

Ou:

[1,3,7,"436",354,nil].except([354, 1]) #=> [3,7,"436",nil]

11
Você não precisa do value.kind_of(Array)teste. Apenas use self - Array(value).
precisa saber é o seguinte

3

Portanto, quando você tem várias ocorrências de 3 e deseja excluir apenas a primeira ocorrência de 3, basta fazer o seguinte:

arr = [2, 4, 6, 3, 8, 10, 3, 12]

arr.delete_at arr.index 3

#This will modify arr as [2, 4, 6, 8, 10, 3, 12] where first occurrence of 3 is deleted. Returns the element deleted. In this case => 3.

3

Remoção não destrutiva da primeira ocorrência:

a = [2, 4, 6, 3, 8]
n = a.index 3
a.take(n)+a.drop(n+1)

2

Se você também deseja tornar essa operação de exclusão encadeada, para poder excluir algum item e continuar encadeando operações na matriz resultante, use tap:

[2, 4, 6, 3, 8].tap { |ary| ary.delete(3) }.count #=> 4

1

Compilando todas as opções diferentes para excluir em ruby

delete - Exclui os elementos correspondentes por valor. Se mais de um valor corresponder, ele removerá todos. Se você não se importa com o número de ocorrências ou com certeza sobre uma ocorrência única, use este método.

a = [2, 6, 3, 5, 3, 7]
a.delete(3)  # returns 3
puts a       # return [2, 6, 5, 7]

delete_at - Exclui o elemento no índice especificado. Se você conhece o índice, use este método.

# continuing from the above example
a.delete_at(2) # returns 5
puts a         # returns [2, 6, 7]

delete_if - Exclui todos os elementos para os quais o bloco é verdadeiro. Isso modificará a matriz. A matriz muda instantaneamente conforme o bloco é chamado.

b = [1, 2, 5, 4, 9, 10, 11]
b.delete_if {|n| n >= 10}.  # returns [1, 2, 5, 4, 9]

rejeitar - Isso retornará uma nova matriz com os elementos para os quais o bloco especificado é falso. A ordem é mantida com isso.

c = [1, 2, 5, 4, 9, 10, 11]
c.reject {|n| n >= 10}.  # returns [1, 2, 5, 4, 9]

rejeitar! - o mesmo que delete_if . A matriz pode não mudar instantaneamente quando o bloco é chamado.

Se você deseja excluir vários valores da matriz, a melhor opção é a seguinte.

a = [2, 3, 7, 4, 6, 21, 13]
b = [7, 21]
a = a - b    # a - [2, 3, 4, 6, 13]
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.