2012-10-03 9 views
15

Incluso viniendo de Javascript esto parece atroz a mí:Volver a colocar un elemento en la parte delantera de una matriz en Ruby

irb 
>> a = ['a', 'b', 'c'] 
=> ["a", "b", "c"] 
>> a.unshift(a.delete('c')) 
=> ["c", "a", "b"] 

¿Existe una manera más legible la colocación de un elemento a la parte delantera de una matriz?

Editar mi código real:

if @admin_users.include?(current_user) 
    @admin_users.unshift(@admin_users.delete(current_user)) 
end 
+0

el la pregunta no está clara, usted solicita una forma más legible de "colocar un elemento al frente de una matriz" ('Array # insert (index, value)'?) pero el ejemplo usa 'delete' y parece que usted quería un rotación. – tokland

+0

He reformulado la pregunta. – Duopixel

+0

bien, ahora está claro. Sin embargo, ¿tiene que ser una actualización in situ? ¿por qué no devolver una nueva matriz? – tokland

Respuesta

5

Este es un problema más complicado de lo parece. He definido las siguientes pruebas:

describe Array do 
    describe '.promote' do 
    subject(:array) { [1, 2, 3] } 

    it { expect(array.promote(2)).to eq [2, 1, 3] } 
    it { expect(array.promote(3)).to eq [3, 1, 2] } 
    it { expect(array.promote(4)).to eq [1, 2, 3] } 
    it { expect((array + array).promote(2)).to eq [2, 1, 3, 1, 2, 3] } 
    end 
end 

sort_by propuesto por @Duopixel es elegante pero produce [3, 2, 1] para la segunda prueba.

class Array 
    def promote(promoted_element) 
    sort_by { |element| element == promoted_element ? 0 : 1 } 
    end 
end 

@tadman utiliza delete, pero esto elimina todos los elementos coincidentes, por lo que la salida de la cuarta prueba es [2, 1, 3, 1, 3].

class Array 
    def promote(promoted_element) 
    if (found = delete(promoted_element)) 
     unshift(found) 
    end 

    self 
    end 
end 

He intentado utilizar:

class Array 
    def promote(promoted_element) 
    return self unless (found = delete_at(find_index(promoted_element))) 
    unshift(found) 
    end 
end 

Pero esto falló la tercera prueba, ya que no puede manejar delete_at nula. Por último, que se establecieron en:

class Array 
    def promote(promoted_element) 
    return self unless (found_index = find_index(promoted_element)) 
    unshift(delete_at(found_index)) 
    end 
end 

¿Quién sabía que una idea tan simple como promote podría ser tan complicado?

+0

Ha pasado un tiempo desde esta pregunta, pero esto se ve bien! – Duopixel

+0

¡Gracias! No podía creer qué tan abajo en el agujero de conejo me llevó un método aparentemente tan simple. – dankohn

+0

Esto es bueno. Solo una observación: su 'promoción' elegida presupone que solo se supone que moverá la primera aparición de 'promotional_element'. 'Array # delete' elimina todos los elementos que coinciden con el parámetro. ¿No sería razonable esperar un comportamiento ortogonal de 'promote' y que el resultado de su cuarta prueba sería '[2, 2, 1, 3, 1, 3]'? Para utilizar su 'promoción' como un componente en un método que mueve todas las apariciones de' promotional_element', se requerirá duplicar repetidamente la matriz y comparar (creo ...). – Huliax

14

Tal Array#rotate iba a funcionar para usted:

['a', 'b', 'c'].rotate(-1) 
#=> ["c", "a", "b"] 
+1

Bueno saberlo. Lamentablemente, no se trata de poner el (los) último (s) elemento (s) al frente, sino tomar un elemento arbitrario y ponerlo al frente. He actualizado mi pregunta y mis disculpas. – Duopixel

+0

No conocía ese método. – Papipo

4

Si por "elegante" que quiere decir más fácil de leer, incluso a expensas de ser no estándar , siempre puedes escribir tu propio método que mejore Array:

class Array 
    def promote(value) 
    if (found = delete(value)) 
     unshift(found) 
    end 

    self 
    end 
end 

a = %w[ a b c ] 
a.promote('c') 
# => ["c", "a", "b"] 
a.promote('x') 
# => ["c", "a", "b"] 

Tenga en cuenta que esto solo cambiaría la posición de una sola instancia de un valor. Si hay varios en el conjunto, los siguientes probablemente no se moverán hasta que se elimine el primero.

+0

No estoy familiarizado con la etiqueta de extender clases nativas en Ruby. ¿Esto se consideraría una mala forma ya que está en javascript? – Duopixel

+2

Ruby on Rails tiene una enorme cantidad de extensiones para las clases principales de Ruby, por lo que se ha convertido en una especie de tradición. Realmente depende. Hay una línea fina entre inteligente y * demasiado * inteligente. Si realiza esta operación en muchos lugares, tendría sentido. Si en una sola, me quedaría con lo que tienes. – tadman

11

Tal vez esto se ve mejor a usted:

a.insert(0, a.delete('c')) 
+0

Esta es una gran solución cuando se garantiza que la matriz fuente es única. La respuesta aceptada arriba cubre más escenarios; Sin embargo, si la matriz no contiene duplicados, es más rápido y más fácil de implementar que el anterior: en mi humilde opinión, por supuesto. – danielricecodes

3

Al final he considerado esta la alternativa más legible para mover un elemento a la parte delantera:

if @admin_users.include?(current_user) 
    @admin_users.sort_by{|admin| admin == current_user ? 0 : 1} 
end 
0

Si todos los elementos de la matriz son únicos puede utilizar matriz aritmética:

> a = ['a', 'b', 'c'] 
=> ["a", "b", "c"] 
> a -= "c" 
=> ["a", "b"] 
> a = ["c"] + a 
=> ["c", "a", "b"] 
1

Adición de mis dos centavos:

array.select{ |item| <condition> } | array 

Pros:

  • puede mover varios elementos al frente de la matriz

Contras:

  • Esto eliminará todos los duplicados a menos que sea el resultado deseado.

Ejemplo - Mover todos impares los números en la parte delantera (y hacer gama única):

data = [1, 2, 3, 4, 3, 5, 1] 
data.select{ |item| item.odd? } | data 
# Short version: 
data.select(&:odd?) | data 

Resultado:

[1, 3, 5, 2, 4] 
0

Otra forma:

a = [1, 2, 3, 4] 
b = 3 

[b] + (a - [b]) 
=> [3, 1, 2, 4] 
Cuestiones relacionadas