devzone
devzone

Reputation: 49

return keys from array having same values in ruby

How to find out elements of array having same value_entries. As code is in ruby, looking better approach.

Input

"block_device": {
  "sda": {
    "size": "83886080",
    "removable": "0",
    "model": "VBOX HARDDISK",
    "rev": "1.0",
    "state": "running",
    "timeout": "30",
    "vendor": "ATA",
    "rotational": "1"
  },
  "sdb": {
    "size": "16384",
    "removable": "0",
    "model": "VBOX HARDDISK",
    "rev": "1.0",
    "state": "running",
    "timeout": "30",
    "vendor": "ATA",
    "rotational": "1"
  },
  "sdc": {
    "size": "16384",
    "removable": "0",
    "model": "VBOX HARDDISK",
    "rev": "1.0",
    "state": "running",
    "timeout": "30",
    "vendor": "ATA",
    "rotational": "1"
  }
}

Sample Code Block:

devicesForRaid = []
deviceHolder = []
node['block_device'].each do |deviceName,deviceProperty|
   deviceHolder.push(deviceName,deviceProperty['size'])       #['sda'=>'83886080','sdb'=>'16384','sdc'=>'16384']
end

deviceHolder.each do | deviceName,deviceSize|

    # how to get deviceName who all having same size

    if(deviceSize_match_found){
        devicesForRaid.push(deviceName)
    }
end

Expected Output:

devicesForRaid = ['sdb','sdc']

Trial way:

using stack, push 1st element onto stack, and comparing with rest of array element.

if match found, push that element onto stack.

Sample code block completion or better code highly appreciated.

Upvotes: 1

Views: 349

Answers (4)

itmammoth
itmammoth

Reputation: 101

How about using group_by?

node[:block_device]
  .group_by {|device, attributes| attributes[:size] }
  .map {|size, devices| devices.size > 1 ? devices.map(&:first) : nil }
  .compact
  .flatten
=> [:sdb, :sdc]

I think this way is easy to understand what you are doing.

Upvotes: 0

K M Rakibul Islam
K M Rakibul Islam

Reputation: 34328

You can do it this way (assuming block_device is a key in your input data hash):

hash = input_data[:block_device]
new_hash = Hash.new{ |h, k| h[k] = [] }
hash.each do |k, v|
  new_hash[v[:size]] << k
end
p new_hash
# => {"83886080"=>[:sda], "16384"=>[:sdb, :sdc]}

From this new_hash, you can extract your required data easily.

e.g. if you want to extract the elements that has a size more than 1, you can do this:

p new_hash.select { |k,v| v.length > 1 }.values.flatten
# => [:sdb, :sdc]

Upvotes: 0

Cary Swoveland
Cary Swoveland

Reputation: 110675

You can do this:

input_hash[:block_device].each_with_object({}) { |(k,g),h|
  h.update(g[:size]=>[k]) { |_,o,n| o+n } }
  #=> {"83886080"=>[:sda], "16384"=>[:sdb, :sdc]} 

This uses the form of Hash#update (aka merge!) that employs the block:

{ |_,o,n| o+n }

to determine the values of keys that are present in both hashes being merged.

Upvotes: 2

John La Rooy
John La Rooy

Reputation: 304147

res = Hash.new { |h, k| h[k] = [] }
node['block_device'].each{|k, v| res[v[:size]]<<k}

gives:

=> {"83886080"=>[:sda], "16384"=>[:sdb, :sdc]}

I guess you want to look through res for values with length of 2 or more

res.to_a.select{|k, v| v.size > 1}

Upvotes: 1

Related Questions