Remove one object from an array with multiple matching objects

I have an array:

array = ['a', 'b', 'c', 'a', 'b', 'a', 'a']

      

sorted for easier viewing:

array = ['a', 'a', 'a', 'a', 'b', 'b', 'c']

      

I want to remove, for example, three from a

. array.delete('a')

deletes everything a

.

The following code "works", but I think you will agree to it absolutely disgusting.

 new_array = array.sort.join.sub!('aaa', '').split(//)

      

How can I make it cleaner?

To give a little more information on what I am doing here, I have a few lines that are being inserted into an array asynchronously. These strings can (and often are) identical to each other. If there are a certain number of matching lines, the action is triggered, the corresponding object is removed (such as Tetris), and the process continues.

Before executing the following code, there array

might be ['a', 'a', 'a', 'b']

:

while array.count(product[:name]) >= product[:quantity]
  # trigger an event
  product[:quantity].times do
    array.slice!(array.index(product[:name]))
  end
end

      

assuming there product[:name]

is a

, and product[:quantity]

- 3

, after running the above code, the array should be ['b']

.

+3


source to share


3 answers


I think you have an XY problem. Instead of an array, you must use a hash with the number of occurrences as the value.

hash = Hash.new(0)

      

If you want to add an entity, you must do:



hash["a"] += 1

      

If you want to limit the number to a certain value, say k, then do:

hash["a"] += 1 unless hash["a"] == k

      

+2


source


slice

maybe what you are looking for:



3.times {array.slice!(array.index('a'))}

      

+2


source


If you want to store or convert an array so that it only contains one instance of each element, you can use uniq

or Set instead of an array.

array = ['a', 'b', 'c', 'a', 'b', 'a', 'a']
array.uniq # => ["a", "b", "c"]

require 'set'
array.to_set # => #<Set: {"a", "b", "c"}>

      

The set will automatically preserve the uniqueness of all the elements for you, which is useful if you are going to have a huge number of potentially repeating elements and do not want to accumulate them in memory before making uniq

on them.


@sawa mentioned it looks like an XY problem, "and I agree.

The source of the problem is the use of an array instead of a hash as the main container. An array is good when you have a queue or list of things to process in order, but it's terrible when you need to keep track of the number of things because you have to traverse that array to find out how much of a certain thing you have.There are ways to force information which you want to get from the array when you get the array as source.

Since it looks like he identified the real problem, here are some building blocks that can be used around the problem.

If you have an array and want to figure out how many different elements there are and their count:

array = ['a', 'a', 'a', 'a', 'b', 'b', 'c', 'c']
array_count = array.group_by { |i| i }.map{ |k, v| [k, v.size] }.to_h
# => {"a"=>4, "b"=>2, "c"=>2}

      

From this point, it is easy to find out which ones exceed a certain score:

array_count.select{ |k, v| v >= 3 } # => {"a"=>4}

      

To quickly remove all elements from an array, after processing you can use a set of difference ":

array = ['a', 'a', 'a', 'a', 'b', 'b', 'c']
array -= ['a']
# => ["b", "b", "c", "c"]

      

or delete_if

:

array.delete_if { |i| i == 'a' }
array # => ["b", "b", "c"]

      

+2


source







All Articles