1

I have an array (array1) of hashes that looks like this :

array1 = [ {:ID=>"1", :value=>"abc"}, {:ID=>"2", :value=>"def"} ] 

I can loop over each hash and check each hash value manually:

array1.each do |h| if h.has_value?("def") #do something end end 

Is there a way to check if the hash value "abc" exists within a hash inside the array without having to iterate over the array?

4
  • 1
    Yes, don't use an array. It takes away the O(1) point of lookup for hash tables. Just use a JS Object literal for this. Commented Apr 21, 2017 at 20:34
  • Where does array1 come from, how is it generated? Is it possible to change the structure (for example to an hash) at the origin of array1? Commented Apr 21, 2017 at 21:27
  • Do you consider "1" to be a value inside a hash of your array? Commented Apr 21, 2017 at 22:35
  • yes, by definition "1" and "abc" are values of the hash. Where "ID" and "value" are keys. Commented Apr 22, 2017 at 6:47

4 Answers 4

2

find method is the most concise way with an array.

array1.find { |item| item[:value] == 'abc' } 

Anyway, if you can start directly with an hash is better but if you have to switch to an hash from an array to have the O(1) lookup, it will probably be slower anyway.

Sign up to request clarification or add additional context in comments.

Comments

2

use any? instead of each, it breaks in early stage if found, best case O(1) and worst case O(n)

word = "def" is_present = array1.any? { |h| h.has_value?(word) } 

is_present is true if word is found in hash otherwise false

7 Comments

But this iterates over the array, which the OP doesn't want to do and is still O(n).
yes, you have to iterate ultimately, but ruby has any? which ease our work, how will u find element in array by iterating right.
@SpencerWieczorek There is no avoiding iteration, however any? will stop processing at the first true value, avoiding a full array scan.
@MarkThomas You could certainly avoid iteration, OP is asking for a solution to an XY Problem.
@SpencerWieczorek Agreed. An array is not the best starting point.
|
1

One way or another, you can't avoid iterating over the array. Here's one way.

array1.flat_map(&:values).include?("def") #=> true 

Note

array1.flat_map(&:values) #=> ["1", "abc", "2", "def"] 

1 Comment

From the question, I don't know if "1" and "2" should be here.
0

You obviously have to iterate at least once over the elements of your array.

If you do this often, you should use another data format:

array1 = [ { ID: '1', value: 'abc' }, { ID: '2', value: 'def' }, { ID: '3', value: 'abc' } ] lookup_table = array1.each_with_object(Hash.new { |h, k| h[k] = [] }) do |hash, table| table[hash[:value]] << hash[:ID] end p lookup_table # {"abc"=>["1", "3"], "def"=>["2"]} p lookup_table['abc'] # ["1", "3"] 

As a bonus, it gives you all the IDs for which this value is found, and it gives it fast.

If you just want to know if there's a value somewhere in your hashes, you could use a Set:

require 'set' all_values = Set.new(array1.map{|h| h[:value]}) p all_values.include? 'abc' # true p all_values.include? 'xyz' # false 

Again, the lookup will be much faster than with an array of hashes.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.