How can I get a key => value out of this foursquare hash? - ruby

Here is what it looks like:
{
"groups" => [
{ "venues" => [
{ "city" => "Madrid",
"address" => "Camino de Perales, s/n",
"name" => "Caja Mágica",
"stats" => {"herenow"=>"0"},
"geolong" => -3.6894333,
"primarycategory" => {
"iconurl" => "http://foursquare.com/img/categories/arts_entertainment/stadium.png",
"fullpathname" => "Arts & Entertainment:Stadium",
"nodename" => "Stadium",
"id" => 78989 },
"geolat" => 40.375045,
"id" => 2492239,
"distance" => 0,
"state" => "Spain" }],
"type" => "Matching Places"}]
}
Big and ugly... I just want to grab the id out. How would I go about doing this?

h = { "groups" => ......... }
The two ids are:
h["groups"][0]["venues"][0]["primarycategory"]["id"]
h["groups"][0]["venues"][0]["id"]

If the hash stores one id:(assuming the value is stored in a variable called hash)
hash["groups"][0]["venues"][0]["primarycategory"]["id"] rescue nil
If the hash stores multiple ids then:
ids = Array(hash["groups"]).map do |g|
Array(g["venues"]).map do |v|
v["primarycategory"]["id"] rescue nil
end.compact
end.flatten
The ids holds the array of id's.

Related

Accessing a hash element that has a value of false specificially

I have a hash that i use in watir automation and it returns a true/false value based on the presence of elements in a UI. Rather than returning the entire hash, can i just return anything that evaluates to false?
elements = {
"Title" => #b.title == 'Details',
"Name" => #b.div(:class => 'rpt-name').present?,
"Address" => #b.div(:class => 'rpt-address').present?,
"Stats" => #b.div(:class => 'rpt-stats-container-top').present?,
"Employee Information" => #b.div(:class => 'rpt-stats-container').present?,
"Reports" => #b.div(:class => 'rpt-ribbon-container').present?
}
if elements.values.include? false
puts "ERROR: Page Validation Failed. #{elements.inspect}"
valid = false
else
valid = true
end
valid
you can use select and return all values that are false
elements.select {|_key, value| !value }
your code would be like:
elements = {
"Title" => #b.title == 'Details',
"Name" => #b.div(:class => 'rpt-name').present?,
"Address" => #b.div(:class => 'rpt-address').present?,
"Stats" => #b.div(:class => 'rpt-stats-container-top').present?,
"Employee Information" => #b.div(:class => 'rpt-stats-container').present?,
"Reports" => #b.div(:class => 'rpt-ribbon-container').present?
}.select { |_key, value| !value }

Convert flat list of products and categories to tree structure

I currently have items in the following structure:
[{
"category" => ["Alcoholic Beverages", "Wine", "Red Wine"],
"name" => "Robertson Merlot",
"barcode" => '123456789-000'
"wine_farm" => "Robertson Wineries",
"price" => 60.00
}]
I have made up this data, but the data I am using is in the same structure and I cannot change the data coming in.
I have > 100 000 of these.
Each product is nested between 1 and n (unlimited) categories.
Because of the tabular nature of this data, the categories are repeated. I want to use tree data to prevent this repetition and cut down the file size by 25 to 30%.
I am aiming at a tree structure something like this:
{
"type" => "category",
"properties" => {
"name" => "Alcoholic Beverages"
},
"children" => [{
"type" => "category",
"properties" => {
"name" => "Wine"
},
"children" => [{
"type" => "category",
"properties" => {
"name" => "Red Wine"
},
"children" => [{
"type" => "product",
"properties" => {
"name" => "Robertson Merlot",
"barcode" => '123456789-000',
"wine_farm" => "Robertson Wineries",
"price" => 60.00
}
}]
}]
}]
}
I can't seem to think of an efficient algorithm to get this right. I would appreciate any help in the right direction.
Should I be generating ID's and ad the parent ID for each node? I am concerned that using ID's will add more length to the text, which I am trying to shorten.
Although I have simplified it a bit from your requested structure, you can use the logic to get an idea of how it could be done:
require 'pp'
x = [{
"category" => ["Alcoholic Beverages", "Wine", "Red Wine"],
"name" => "Robertson Merlot",
"barcode" => '123456789-000',
"wine_farm" => "Robertson Wineries",
"price" => 60.00
}]
result = {}
x.each do |entry|
# Save current level in a variable
current_level = result
# We want some special logic for the last item, so let's store that.
item = entry['category'].pop
# For each category, check if it exists, else add a category hash.
entry['category'].each do |category|
unless current_level.has_key?(category)
current_level[category] = {'type' => 'category', 'children' => {}, 'name' => category}
end
current_level = current_level[category]['children'] # Set the new current level of the hash.
end
# Finally add the item:
entry.delete('category')
entry['type'] = 'product'
current_level[item] = entry
end
pp result
And it gives us:
{"Alcoholic Beverages"=>
{"type"=>"category",
"children"=>
{"Wine"=>
{"type"=>"category",
"children"=>
{:"Red Wine"=>
{"name"=>"Robertson Merlot",
"barcode"=>"123456789-000",
"wine_farm"=>"Robertson Wineries",
"price"=>60.0,
"type"=>"product"}},
"name"=>"Wine"}},
"name"=>"Alcoholic Beverages"}}
There are probably easier ways of doing this but this is all I can think of for now, it should match your structure.
require 'json'
# Initial set up, it seems the root keys are always the same looking at your structure
products = {
'type' => 'category',
'properties' => {
'name' => 'Alcoholic Beverages'
},
'children' => []
}
data = [{
"category" => ['Alcoholic Beverages', 'Wine', 'Red Wine'],
"name" => 'Robertson Merlot',
"barcode" => '123456789-000',
"wine_farm" => 'Robertson Wineries',
"price" => 60.00
}]
data.each do |item|
# Make sure we set the current to the top-level again
curr = products['children']
# Remove first entry as it's always 'Alcoholic Beverages'
item['category'].shift
item['category'].each do |category|
# Get the index for the category if it exists
index = curr.index {|x| x['type'] == 'category' && x['properties']['name'] == category}
# If it exists then change current hash level to the child of that category
if index
curr = curr[index]['children']
# Else add it in
else
curr << {
'type' => 'category',
'properties' => {
'name' => category
},
'children' => []
}
# We can use last as we know it'll be the last index.
curr = curr.last['children']
end
end
# Delete category from the item itself
item.delete('category')
# Add the item as product type to the last level of the hash
curr << {
'type' => 'product',
'properties' => item
}
end
puts JSON.pretty_generate(products)

Select a value from a nested hash with unknown keys

I have a hash that looks something like this:
hash = { "data" => {
"Aatrox" => {
"id" => "Aatrox",
"key" => "266",
"name" => "Aatrox"
},
"Ahri" => {
"id" => "Ahri",
"key" => "123",
"name" => "Ahri"
},
"Another name" => {
"id" => "Another name",
"key" => "12",
"name" => "Another name"
},
}
}
I'm trying to get the value from "id" that matches a given key:
def get_champion_name_from_id(key)
filtered = #champion_data["data"].select do | key, champ_data |
Integer(champ_data["key"]) == key
end
end
I'm using select to get the items that match the block, however, the return value is another hash that looks like this:
{
"Aatrox": {
"id" => "Aatrox",
"key" => "266",
"name" => "Aatrox"
}
}
How can I avoid this and get just the last nested hash?
If the key passed was 266, I want to get this hash:
{
"id" => "Aatrox",
"key" => "266",
"name" => "Aatrox"
}
This hash is the result of a parsed JSON file, so there's no way I can do filtered["Aatrox"] to get a given value.
Hash#values returns values only (without keys). And by using Enumerable#find, you will get the first matched item instead of an array that contains a single item.
#champion_data['data'].values.find { |champ_data|
champ_data['key'] == '266'
}
# => {"id"=>"Aatrox", "key"=>"266", "name"=>"Aatrox"}
def get_champion_name_from_id(key)
key = key.to_s
#champion_data['data'].values.find { |champ_data|
champ_data['key'] == key
}
end
You can do it with the select method also:
#champion_data["data"].select do |key, val|
#champion_data["data"][key] if #champion_data["data"][key]["key"] == "266"
end

Understanding attributes in AWS DynamoDB with Ruby

I can't seem to wrap my head around the AWS Ruby SDK documentation for DynamoDB (or more specifically the concepts of the DynamoDB data model).
Specifically I've been reading: http://docs.aws.amazon.com/AWSRubySDK/latest/frames.html#!AWS/DynamoDB.html
Note: I have read through the Data Model documentation as well and it's still not sinking in; I'm hoping a proper example in Ruby with clear up my confusion
In the following code snippet, I create a table called "my_books" which has a primary_key called "item_id" and it's a Hash key (not a Hash/Range combination)...
dyn = AWS::DynamoDB::Client::V20120810.new
# => #<AWS::DynamoDB::Client::V20120810>
dyn.create_table({
:attribute_definitions => [
{ :attribute_name => "item_id", :attribute_type => "N" }
],
:table_name => "my_books",
:key_schema => [
{ :attribute_name => "item_id", :key_type => "HASH" },
],
:provisioned_throughput => {
:read_capacity_units => 10,
:write_capacity_units => 10
}
})
# => {:table_description=>{:attribute_definitions=>[{:attribute_name=>"item_id", :attribute_type=>"N"}], :table_name=>"my_books", :key_schema=>[{:attribute_name=>"item_id", :key_type=>"HASH"}], :table_status=>"ACTIVE", :creation_date_time=>2014-11-24 16:59:47 +0000, :provisioned_throughput=>{:number_of_decreases_today=>0, :read_capacity_units=>10, :write_capacity_units=>10}, :table_size_bytes=>0, :item_count=>0}}
dyn.list_tables
# => {:table_names=>["my_books"]}
dyn.scan :table_name => "my_books"
# => {:member=>[], :count=>0, :scanned_count=>0}
I then try and populate the table with a new item. My understanding is that I should specify the numerical value for item_id (which is the primary key) and then I could specify other attributes for the new item/record/document I'm adding to the table...
dyn.put_item(
:table_name => "my_books",
:item => {
"item_id" => 1,
"item_title" => "My Book Title",
"item_released" => false
}
)
But that last command returns the following error:
expected hash value for value at key item_id of option item
So although I don't quite understand what the hash will be made of, I try doing that:
dyn.put_item(
:table_name => "my_books",
:item => {
"item_id" => { "N" => 1 },
"item_title" => "My Book Title",
"item_released" => false
}
)
But this now returns the following error...
expected string value for key N of value at key item_id of option item
I've tried different variations, but can't seem to figure out how this works?
EDIT/UPDATE: as suggested by Uri Agassi - I changed the value from 1 to "1". I'm not really sure why this has to be quoted as I've defined the type to be a number and not a string, but OK let's just accept this and move on.
I've finally figured out most of what I needed to understand the data model of DynamoDB and using the Ruby SDK.
Below is my example code, which hopefully will help someone else, and I've got a fully fleshed out example here: https://gist.github.com/Integralist/9f9f2215e001b15ac492#file-3-dynamodb-irb-session-rb
# https://github.com/BBC-News/alephant-harness can automate the below set-up when using Spurious
# API Documentation http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Operations.html
# Ruby SDK API Documentation http://docs.aws.amazon.com/AWSRubySDK/latest/frames.html#!AWS/DynamoDB/Client/V20120810.html
require "aws-sdk"
require "dotenv"
require "spurious/ruby/awssdk/helper"
Spurious::Ruby::Awssdk::Helper.configure
# => <AWS::Core::Configuration>
Dotenv.load(
File.join(
File.dirname(__FILE__), "config", "development", "env.yaml"
)
)
# => {"AWS_REGION"=>"eu-west-1", "AWS_ACCESS_KEY_ID"=>"development_access", "AWS_SECRET_ACCESS_KEY"=>"development_secret", "DYNAMO_LU"=>"development_lookup", "DYNAMO_SQ"=>"development_sequence", "SQS_QUEUE"=>"development_queue", "S3_BUCKET"=>"development_bucket"}
dyn = AWS::DynamoDB::Client.new :api_version => "2012-08-10"
dyn = AWS::DynamoDB::Client::V20120810.new
# => #<AWS::DynamoDB::Client::V20120810>
dyn.create_table({
# This section requires us to define our primary key
# Which will be called "item_id" and it must be a numerical value
:attribute_definitions => [
{ :attribute_name => "item_id", :attribute_type => "N" }
],
:table_name => "my_books",
# The primary key will be a simple Hash key (not a Hash/Range which requires both key types to be provided)
# The attributes defined above must be included in the :key_schema Array
:key_schema => [
{ :attribute_name => "item_id", :key_type => "HASH" }
],
:provisioned_throughput => {
:read_capacity_units => 10,
:write_capacity_units => 10
}
})
# => {:table_description=>{:attribute_definitions=>[{:attribute_name=>"item_id", :attribute_type=>"N"}], :table_name=>"my_books", :key_schema=>[{:attribute_name=>"item_id", :key_type=>"HASH"}], :table_status=>"ACTIVE", :creation_date_time=>2014-11-24 16:59:47 +0000, :provisioned_throughput=>{:number_of_decreases_today=>0, :read_capacity_units=>10, :write_capacity_units=>10}, :table_size_bytes=>0, :item_count=>0}}
dyn.list_tables
# => {:table_names=>["my_books"]}
dyn.scan :table_name => "my_books"
# => {:member=>[], :count=>0, :scanned_count=>0}
dyn.put_item(
:table_name => "my_books",
:item => {
"item_id" => { "N" => "1" }, # oddly this needs to be a String and not a strict Integer?
"item_title" => { "S" => "My Book Title"},
"item_released" => { "B" => "false" }
}
)
# Note: if you use an "item_id" that already exists, then the item will be updated.
# Unless you use the "expected" conditional feature
dyn.put_item(
:table_name => "my_books",
:item => {
"item_id" => { "N" => "1" }, # oddly this needs to be a String and not a strict Integer?
"item_title" => { "S" => "My Book Title"},
"item_released" => { "B" => "false" }
},
# The :expected key specifies the conditions of our "put" operation.
# If "item_id" isn't NULL (i.e. it exists) then our condition has failed.
# This means we only write the value when the key "item_id" hasn't been set.
:expected => {
"item_id" => { :comparison_operator => "NULL" }
}
)
# AWS::DynamoDB::Errors::ConditionalCheckFailedException: The conditional check failed
dyn.scan :table_name => "my_books"
# => {:member=>[{"item_id"=>{:n=>"1"}, "item_title"=>{:s=>"My Book Title"}, "item_released"=>{:b=>"false"}}], :count=>1, :scanned_count=>1}
dyn.query :table_name => "my_books", :consistent_read => true, :key_conditions => {
"item_id" => {
:comparison_operator => "EQ",
:attribute_value_list => [{ "n" => "1" }]
},
"item_title" => {
:comparison_operator => "EQ",
:attribute_value_list => [{ "s" => "My Book Title" }]
}
}
# => {:member=>[{"item_id"=>{:n=>"1"}, "item_title"=>{:s=>"My Book Title"}, "item_released"=>{:b=>"false"}}], :count=>1, :scanned_count=>1}
dyn.query :table_name => "my_books",
:consistent_read => true,
:select => "SPECIFIC_ATTRIBUTES",
:attributes_to_get => ["item_title"],
:key_conditions => {
"item_id" => {
:comparison_operator => "EQ",
:attribute_value_list => [{ "n" => "1" }]
},
"item_title" => {
:comparison_operator => "EQ",
:attribute_value_list => [{ "s" => "My Book Title" }]
}
}
# => {:member=>[{"item_title"=>{:s=>"My Book Title"}}], :count=>1, :scanned_count=>1}
dyn.delete_item(
:table_name => "my_books",
:key => {
"item_id" => { "n" => "1" }
}
)
# => {:member=>[], :count=>0, :scanned_count=>0}

Using Ruby, how do I print just the value of a MongoDB query result

I want to print just the value of the field in question (name). What it does now is print the following.
nameHOSTA
nameHOSTB
I want it to print
HOSTA
HOSTB
Query ...
puts collection.find({"environment_name" => role, "type" => "TF"}, {:fields => {"_id" => 0, "name" =>
1}}).to_a
I think something like this should get you want you want:
collection.find({"environment_name" => role, "type" => "TF"}, {:fields => {"_id" => 0, "name" =>1}}).each { |item|
puts item['name']
}

Resources