Extract keys from string hash - arrays

I have the following hash string:
arr = ["{:id=>1, :name=>\"a\"}", "{:id=>2, :name=>\"b\"}"]
How can I parse the data and get all id keys in a array like this: [1,2]
Thanks.

Since this array of strings doesn't seem to be exactly valid JSON, you can parse out the ids by doing something like the following:
arr = ["{:id=>1, :name=>\"a\"}", "{:id=>2, :name=>\"b\"}"]
arr.map { |x| x.split(":id=>").last.split(",").first.to_i }
# => [1, 2]
Hope this helps!

Looks like Ruby hashes which need to be evaluated, so use eval within a map:
arr.map{|hash| eval(hash)[:id]}

This is an array of JSON strings. You can use JSON.parse to parse them
arr = ["{:id=>1, :name=>\"a\"}", "{:id=>2, :name=>\"b\"}"]
ids = arr.map do |raw| # iterate on each string
JSON.parse(raw)[:id] # parse and extract the id
end
puts ids # => [1,2]

Related

Problem to convert Array of a string to ruby object

I tried to pass array of string to use puts in ruby print it value with math operator. But get the wrong result. I thingk it need to convert to object to achieve this as follow.
my_string_array = ["100*(4+1)"]
my_string = my_string_array.join(' ') # => "100*(4+1)"
my_ruby_valuation = my_string.to_i # => 100 (Wrong)
What I expected is
puts my_ruby_valuation #=> 500
You should use eval kernel function.
https://apidock.com/ruby/Kernel/eval
my_string_array = ["100*(4+1)"]
eval(my_string_array[0])
I hope this helps.

Groovy Convert string data to map

I have a large String which I want to convert to a Map in groovy.
The String data is an array of key value pairs each key and value is enclosed in square brackets [] and separated by commas. Full data string here: https://pastebin.com/raw/4rBWRzMs
Some of the values can be empty e.g. '[]' or a list of values containing , and : characters e.g.
[1BLL:220,1BLE:641,2BLL:871,2BLE:475,SW:10029,KL:0,KD:78,ODT:148,AVB:358]
I only want to split on these characters if they are not enclosed in square brackets [].
The code I have tried but breaks when there are a list of values. Is there a better way? Thanks.
String testData="[[DEVICE_PROVISIONED]: [1], [aaudio.hw_burst_min_usec]: [2000],[debug.hwui.use_buffer_age]: [false], [ro.boot.boottime][1BLL:220,1BLE:641,2BLL:871,2BLE:475,SW:10029,KL:0,KD:78,ODT:148,AVB:358], ro.boot.hardware]: [walleye],[dev.mnt.blk.postinstall]: [],[ro.boot.usbradioflag]: [0], [ro.boot.vbmeta.avb_version]: [1.0], [ro.boot.vbmeta.device]: [/dev/sda18], [ro.boot.vbmeta.device_state]: [unlocked]]"
def map = [:]
testData.replaceAll('\\[]','null').replaceAll("\\s","").replaceAll('\\[','').replaceAll(']','').split(",").each {param ->
def nameAndValue = param.split(":")
map[nameAndValue[0]] = nameAndValue[1]
}
I'd grep the key-value-tuples from that format and build a map from
there. Once this is done it's easier to deal with further
transformations. E.g.
def testData="[DEVICE_PROVISIONED]: [1], [aaudio.hw_burst_min_usec]: [2000],[debug.hwui.use_buffer_age]: [false], [ro.boot.boottime]: [1BLL:220,1BLE:641,2BLL:871,2BLE:475,SW:10029,KL:0,KD:78,ODT:148,AVB:358], [ro.boot.hardware]: [walleye],[dev.mnt.blk.postinstall]: [],[ro.boot.usbradioflag]: [0], [ro.boot.vbmeta.avb_version]: [1.0], [ro.boot.vbmeta.device]: [/dev/sda18], [ro.boot.vbmeta.device_state]: [unlocked]"
def map = [:]
(testData =~ /\s*\[(.*?)\]\s*:\s*\[(.*?)\]\s*,?\s*/).findAll{ _, k, v ->
map.put(k,v)
}
println map.inspect()
// → ['DEVICE_PROVISIONED':'1', 'aaudio.hw_burst_min_usec':'2000', 'debug.hwui.use_buffer_age':'false', 'ro.boot.boottime':'1BLL:220,1BLE:641,2BLL:871,2BLE:475,SW:10029,KL:0,KD:78,ODT:148,AVB:358', 'ro.boot.hardware':'walleye', 'dev.mnt.blk.postinstall':'', 'ro.boot.usbradioflag':'0', 'ro.boot.vbmeta.avb_version':'1.0', 'ro.boot.vbmeta.device':'/dev/sda18', 'ro.boot.vbmeta.device_state':'unlocked']
Note that I have fixed some syntax in the testData and removed the outer
[]. If the original testData are actually containing invalid syntax
to the rules given, then this will not work.

Ruby join two arrays by key value

I have two arrays, the first array contains field name, type and id.
arr1 = [
{
"n" => "cat",
"t" => 0,
"id" => "42WTd5"
},
{
"n" => "dog",
"t" => 0,
"id" => "1tM5T0"
}
]
Second array contains field, id and value.
arr2 = [
{
"42WTd5"=>"meow",
"1tM5T0"=>"woof"
}
]
How can I join them by id to produce the following result.
cat: meow
dog: woof
Any help is appreciated.
I think you want your result to be a Hash, in which case this would do the job:
def match_animals_to_sounds(animal_array, sound_array)
sounds = sound_array.first
animal_array.map { |animal| [animal['n'], sounds[animal['id']]] }.to_h
end
>> match_animals_to_sounds(arr1, arr2)
=> {"cat"=>"meow", "dog"=>"woof"}
Your arr2 is unusual in that it is an Array of a single element. I'm just calling #first on it to pull out the Hash inside. If you expect some version of this Array to have more than one element in the future, you'll need to rethink the first line of this method.
The second line is standard Ruby Array manipulation. The first part maps each animal to a new Array of two-element Arrays containing each animal's name and sound. At the end, #to_h converts this array of two-element arrays to a single Hash, which is much more useful than an array of strings. I don't know what you intended in your question, but this is probably what you want.
If you prefer to work with Symbols, you can change the second line of the method to:
animal_array.map { |animal| [animal['n'].to_sym, sounds[animal['id']].to_sym] }.to_h
In which case you will get:
>> match_animals_to_sounds(arr1, arr2)
=> {:cat=>:meow, :dog=>:woof}
This is a way to do it.
sounds = arr2[0]
results = arr1.map do |animal|
"#{animal["n"]}: #{sounds[animal["id"]]}"
end
puts results
# => cat: meow
# => dog: woof
Seems like the second array should just be a hash instead. There's no point creating an array if there's only one element in it and that number won't change.
pointless one-liner (don't use this)
puts arr1.map { |x| "#{x["n"]}: #{arr2[0][x["id"]]}" }
You can also get the join result by following code
arr1.collect{ |a| {a["n"] => arr2[0][a["id"]]} }

ruby how to add a hash element into an existing json array

I have code that outputs the following:
car_id = ["123"]
model = [:model].product(car_id).map { |k,v| {k=>v} }
model = [{:model=>"123"}]
I would like to then add a new hash :make into the json like this:
model_with_make = [{:model=>"123", :make => "acura"}]
How do I do this?
Unfortunately, every solution I find produces this:
[{:model=>"123"}, {:make => "acura"}] and that is not what I want.
Ruby convention for showing result of a calculation
I assume you mean that
model = [:model].product(car_id).map { |k,v| {k=>v} }
produces an array containing a single hash:
[{:model=>"123"}]
and that you are not then executing the statement:
model = [{:model=>"123"}]
which would overwrite the the value of model from the previous statement, rendering the previous statement meaningless. If so, the normal way to write that is as follows.
model = [:model].product(car_id).map { |k,v| {k=>v} }
#=> [{:model=>"123"}]
Computing the array
Next,
arr = [:model].product(car_id)
#=> [[:model, "123"]]
But why use Array#product when the arrays [:model] and car_id both contain a single element? It's simpler to just write
arr = [[:model, car_id.first]]
#=> [[:model, "123"]]
Converting the array to a hash
Since arr contains only one element, there's not much point to mapping it; just convert it to a hash:
Hash[arr]
#=> {:model=>"123"}
or (for Ruby versions 1.9+):
arr.to_h
#=> {:model=>"123"}
Add a key-value pair to the hash
If you wish to add the key-value pair :make => "acura" to
h = {:model=>"123"}
you can simply write
h[:make] = "acura"
#=> "acura"
h #=> {:model=>"123", :make=>"acura"}
or, in one line,
(h[:make] = "acura") && h
#=> {:model=>"123", :make=>"acura"}
Wrapping up
Putting this together, you could write
h = [[:model, car_id.first]].to_h
#=> {:model=>"123"}
(h[:make] = "acura") && h
#=> {:model=>"123", :make=>"acura"}
model = [{:model=>"123"}]
model_with_make = model.map { |m| m.merge(make: 'acura') }
#⇒ [{:model=>"123", :make => "acura"}]
If you are concerned that there is the only element in the array model, you might modify it inplace:
model.first.merge!(make: 'acura')
model
#⇒ [{:model=>"123", :make => "acura"}]

How to parse single-string array to hash in Ruby?

I have a Ruby array (don't ask me why it is not hash already, i can't do anything about it):
[{":cost=>100", ":size=>2"}]
What do i need to do to make it the classical Ruby hash with keys and values?
What is my best option? Maybe there is some libraries for this kind of operations?
Thanks.
First we need to clean the string to make it look like a valid array:
my_string = my_string[2..-3]
my_array = eval("[#{my_string}]")
Now you can join the strings, and then eval it into a hash:
elements = my_array.join(', ')
my_hash = eval("{ #{ elements } }")
(this can be done in fewer lines, but I separated them for clarity)
You could use the JSON module to do that. That would arguably be safer than by using eval.
To see how JSON could be used, let's do some reverse-engineering. You want to create a hash:
h = { :cost=>100, :size=>2 }
from the string:
str = '[{":cost=>100", ":size=>2"}]'
#=> "[{\":cost=>100\", \":size=>2\"}]"
Let's see how that hash would be encoded as a JSON string:
require 'json'
jstr = JSON.generate(h)
#=> "{\"cost\":100,\"size\":2}"
Once we have jstr (which is nothing more than a string) we can extract the desired hash:
JSON.parse(jstr)
#=> {"cost"=>100, "size"=>2}
so the task reduces to converting str to jstr:
"[{\":cost=>100\", \":size=>2\"}]" => "{\"cost\":100,\"size\":2}"
Perhaps the easiest way is to first pull out the keys and values, which we can do with a regex:
r = /
( # start capture group 1
[a-z] # match a lowercase letter
\w* # match >= 0 word characters
) # close capture group 1
=> # match characters
(-?\d+) # optionally match a minus sign followed by > 0 digits in
# capture group 2
/x # free-spacing regex definition mode
arr = str.scan r
#=> [["cost", "100"], ["size", "2"]]
We can now form jstr:
jstr = "{#{ arr.map { |k,v| "\"#{k}\":#{v}" }.join(",") }}"
#=> "{\"cost\":100,\"size\":2}"
To confirm,
h = JSON.parse(jstr)
#=> {"cost"=>100, "size"=>2}

Resources