ruby add strings from array - ruby-on-rails

I am building a simple breadcrumb in ruby but I am not sure how to really implement my logic.
Let's say I have an array of words that are taken from my request.path.split("/) ["", "products", "women", "dresses"]
I want to push the strings into another array so then in the end I have ["/", "/products", "products/women", "products/women/dresses"] and I will use it as my breadcrumb solution.
I am not good at ruby but for now I came up with following
cur_path = request.path.split('/')
cur_path.each do |link|
arr = []
final_link = '/'+ link
if cur_path.find_index(link) > 1
# add all the previous array items with the exception of the index 0
else
arr.push(final_link)
end
end
The results should be ["/", "/products", "/products/women", "/products/women/dresses"]

Ruby's Pathname has some string-based path manipulation utilities, e.g. ascend:
require 'pathname'
Pathname.new('/products/women/dresses').ascend.map(&:to_s).reverse
#=> ["/", "/products", "/products/women", "/products/women/dresses"]

This is my simplest solution:
a = '/products/women/dresses'.split('/')
a.each_with_index.map { |e,i| e.empty? ? '/' : a[0..i].join('/') }

Using map and with_index it can be done like this:
arr = ["", "products", "women", "dresses"]
arr.map.with_index { |item, index| "#{arr[0...index].join('/')}/#{item}" }

This is another option using Enumerable#each_with_object and Enumerable#each_with_index:
ary = '/products/women/dresses'.split('/')
ary[1..].map
.with_index
.with_object([]) { |(folder, idx), path| path << [path[idx-1], folder].join('/') }.unshift('/')
Or also:
(ary.size - 1).times.map { |i| ary.first(i + 2).join('/') }.unshift('/')

Related

How I get the substring?

how can I get the substring of the string:
[{
"ProductId"=>198,
"AttributesXml"=>"<Attributes><ProductAttribute ID=\"590\"><ProductAttributeValue><Value>1691</Value></ProductAttributeValue></ProductAttribute></Attributes>",
"StockQuantity"=>1,
"AllowOutOfStockOrders"=>false,
"Sku"=>nil,
"ManufacturerPartNumber"=>nil,
"Gtin"=>nil,
"OverriddenPrice"=>nil,
"NotifyAdminForQuantityBelow"=>1, "Id"=>1756
},
I want only the value of "ProductAtrribute ID": 590. Only the number.
I have checked out the ruby docs, but I cannot find a solution.
The tag has rails, if you are in rails then you can leverage nokogiri gem to handle parsing the AttributesXml section:
require 'nokogiri'
array = [{"ProductId"=>198, "AttributesXml"=>"<Attributes><ProductAttribute
ID=\"590\"><ProductAttributeValue><Value>1691</Value>
</ProductAttributeValue></ProductAttribute></Attributes>",
"StockQuantity"=>1, "AllowOutOfStockOrders"=>false,"Sku"=>nil,
"ManufacturerPartNumber"=>nil, "Gtin"=>nil, "OverriddenPrice"=>nil,
"NotifyAdminForQuantityBelow"=>1, "Id"=>1756}]
array.each do |item|
doc = Nokogiri::XML(item['AttributesXml'])
puts doc.xpath("//ProductAttribute").attribute('ID')
end
=> 590
I'd use:
require 'nokogiri'
foo = [
{
"ProductId"=>198,
"AttributesXml"=>"<Attributes><ProductAttribute ID=\"590\"><ProductAttributeValue><Value>1691</Value></ProductAttributeValue></ProductAttribute></Attributes>",
"StockQuantity"=>1,
"AllowOutOfStockOrders"=>false,
"Sku"=>nil,
"ManufacturerPartNumber"=>nil,
"Gtin"=>nil,
"OverriddenPrice"=>nil,
"NotifyAdminForQuantityBelow"=>1, "Id"=>1756
},
]
foo.map { |hash|
Nokogiri::XML(hash['AttributesXml']).at('ProductAttribute')['ID']
}
# => ["590"]
It'll return the ID from all <ProductAttribute> nodes in all the hashes in the array.
You can make use of scan
array = [{"ProductId"=>198, "AttributesXml"=>"<Attributes>
<ProductAttribute
ID=\"590\"><ProductAttributeValue><Value>1691</Value>
</ProductAttributeValue></ProductAttribute></Attributes>",
"StockQuantity"=>1, "AllowOutOfStockOrders"=>false,""Sku"=>nil,
"ManufacturerPartNumber"=>nil, "Gtin"=>nil, "OverriddenPrice"=>nil,
"NotifyAdminForQuantityBelow"=>1, "Id"=>1756},.. ]
and
array.each do |hash|
hash["AttributesXml"].scan(/ProductAttribute\s+ID=\"(\d+)\"/).flatten
#=> ["590"]
# This will return matched values
end

How to refactor each function with map in Ruby?

I have a loop building a hash for use in a select field. The intention is to end up with a hash:
{ object.id => "object name", object.id => "object name" }
Using:
#hash = {}
loop_over.each do |ac|
#hash[ac.name] = ac.id
end
I think that the map method is meant for this type of situation but just need some help understanding it and how it works. Is map the right method to refactor this each loop?
Data transformations like this are better suited to each_with_object:
#hash = loop_over.each_with_object({}) { |ac, h| h[ac.name] = ac.id }
If your brain is telling you to use map but you don't want an array as the result, then you usually want to use each_with_object. If you want to feed the block's return value back into itself, then you want inject but in cases like this, inject requires a funny looking and artificial ;h in the block:
#hash = loop_over.inject({}) { |h, ac| h[ac.name] = ac.id; h }
# -------------------- yuck -----------------------------^^^
The presence of the artificial return value is the signal that you want to use each_with_object instead.
Try:
Hash[loop_over.map { |ac| [ac[:name], ac[:id]] }]
Or if you are running on Ruby 2:
loop_over.map { |ac| [ac[:name], ac[:id]] }.to_h
#hash = Hash[loop_over.map { |ac| {ac.name => ac.id} }.map(&:flatten)]
Edit, a simpler solution as per suggestion in a comment.
#hash = Hash[ loop_over.map { |ac| [ac.name, ac.id] } ]
You can simply do this by injecting a blank new Hash and performing your operation:
loop_over.inject({}){ |h, ac| h[ac.name] = ac.id; h }
Ruby FTW
No a map isn't the correct tool for this.
The general use-case of a map is to take in an array, perform an operation on each element, and spit out a (possibly) new array (not a hashmap) of the same length, with the individual element modifications.
Here's an example of a map
x = [1, 2, 3, 4].map do |i|
i+1 #transform each element by adding 1
end
p x # will print out [2, 3, 4, 5]
Your code:
#hash = {}
loop_over.each do |ac|
#hash[ac.name] = ac.id
end
There is nothing wrong with this example. You are iterating over a list, and populating a hashmap exactly as you wished.
Ruby 2.1.0 introduces brand new method to generate hashes:
h = { a: 1, b: 2, c: 3 }
h.map { |k, v| [k, v+1] }.to_h # => {:a=>2, :b=>3, :c=>4}
I would go for the inject version, but use update in the block to avoid the easy to miss (and therefore error prone) ;h suffix:
#hash = loop_over.inject({}) { |h, ac| h.update(ac.name: ac.id) }

most efficient ruby way to transform this array?

I have an array in the following form:
[\"id\", 545, \"program_name\", \"VILLIANS MARATHON\", \"episode_name\", \"1-Season1:Ep.11\"]
I need to transform it to the form below:
[545, \"VILLIANS MARATHON\", \"1-Season1:Ep.11\"]
The way Im doing this is as follows:
#Convert a Active record hash to a 2D array
def activerecord_hash_to_datatable_array(activerecord_resultset)
array_of_arrays = Array.new()
array_of_rs_hashes = activerecord_resultset.to_a.map(&:serializable_hash)
array_of_rs_hashes.each do |rs|
# {"id"=>1594, "program_name"=>nil, "episode_name"=>nil}
rs = rs.flatten
#[\"id\", 545, \"program_name\", \"MARATHON\", \"episode_name\", \"1-Season1:Ep.11\"]"
rs_array = Array.new()
index = 1
while index < rs.length
puts "index = #{index}"
puts "\033[0;33m"+"#{rs[index]}"+"\033[0;37m"
log_with_yellow("index[#{index}] " + "#{rs[index]}")
rs_array << rs[index]
index += 2
end
array_of_arrays << rs_array
end
array_of_arrays
end
I was wondering what the most efficient way to accomplish this is.
Clearly I need to retain only odd elements. But Id like to avoid iterating over all elements and comparing each elements index.
Is there a way to do this by skipping all the even elements ?
Thanks
You can do the following:
your_array.values_at(*your_array.each_index.select(&:odd?))
=> [545, "VILLIANS MARATHON", "1-Season1:Ep.11"]
require 'json'
arr = JSON.parse("[\"id\", 545, \"program_name\", \"VILLIANS MARATHON\", \"episode_name\", \"1-Season1:Ep.11\"]")
new_arr = arr.select.with_index { |x,i| i.odd? }
p new_arr
# >> [545, "VILLIANS MARATHON", "1-Season1:Ep.11"]
If array_of_rs_hashes is indeed an array of hashes, can't you just do:
res = array_of_rs_hashes.map(&:values)
Yep there is :
require 'json'
Hash[*JSON.parse(s)].values #=> [545, "VILLIANS MARATHON", "1-Season1:Ep.11"]
where s = "[\"id\", 545, \"program_name\", \"VILLIANS MARATHON\", \"episode_name\", \"1-Season1:Ep.11\"]"
Try:
your_2d_array.map {|a| a.each_slice(2).map {|key, value| value}}
If you ahve active support, you can write it slightly more readible:
your_2d_array.map {|a| a.each_slice(2).map(&:second)}

How do I convert an array of hashes into a sorted hash?

If I have an array of hashes, each with a day key:
[
{:day=>4,:name=>'Jay'},
{:day=>1,:name=>'Ben'},
{:day=>4,:name=>'Jill'}
]
What is the best way to convert it to a hash with sorted day values as the keys:
{
:1=>[{:day=>1,:name=>'Ben'}],
:4=>[{:day=>4,:name=>'Jay'},{:day=>4,:name=>'Jill'}]
}
I'm using Ruby 1.9.2 and Rails 3.1.1
Personally, I wouldn't bother "sorting" the keys (which amounts to ordering-by-entry-time in Ruby 1.9) until I actually needed to. Then you can use group_by:
arr = [{:day=>4,:name=>'Jay'}, {:day=>1,:name=>'Ben'}, {:day=>4,:name=>'Jill'}]
arr.group_by { |a| a[:day] }
=> {4=>[{:day=>4, :name=>"Jay"}, {:day=>4, :name=>"Jill"}],
1=>[{:day=>1, :name=>"Ben"}]}
Instead, sort the keys when you actually need them.
Assuming you array is called is list, here's one way using the reduce method:
list.reduce({}) { |hash, item|
(hash[item[:day]] ||= []) << item; hash
}
Here's another using the map method, but you have to carry a holder variable around:
hash = {}
list.each { |item|
(hash[item[:day]] ||= []) << item
}
Once you have the unsorted hash say in variable foo, you can sort it as,
Hash[foo.sort]
Simple answer:
data = [
{:day=>4,:name=>'Jay'},
{:day=>1,:name=>'Ben'},
{:day=>4,:name=>'Jill'}
]
#expected solution
sol = {
1=>[{:day=>1,:name=>'Ben'}],
4=>[{:day=>4,:name=>'Jay'},{:day=>4,:name=>'Jill'}]
}
res = {}
data.each{|h|
res[h[:day]] ||= []
res[h[:day]] << h
}
p res
p res == sol #check value
p res.keys == sol.keys #check order
Problem with this solution: The hash is not sorted as requested. (Same problem has Anurags solution).
So you must modify the answer a bit:
res = {}
data.sort_by{|h| h[:day]}.each{|h|
res[h[:day]] ||= []
res[h[:day]] << h
}
p res
p res == sol #check value
p res.keys == sol.keys #check order
In Rails you can use OrderedHash:
ActiveSupport::OrderedHash[arr.group_by { |a| a[:day] }.sort_by(&:first)]
Update: In fact in Ruby 1.9 hash is ordered, so using ActiveSupport extension is not required:
Hash[arr.group_by { |a| a[:day] }.sort_by(&:first)]

Ruby on Rails: Array to Hash with (key, array of values)

Lets say I have an Array of content_categories (content_categories = user.content_categories)
I now want to add every element belonging to a certain categorie to content_categories with the category as a key and the the content-item IDs as elements of a set
In PHP something like this is possible:
foreach ($content_categories as $key => $category) {
$contentsByCategoryIDArray = Category.getContents($category[id])
$content_categories[$key][$contentsByCategoryIDArray]
}
Is there an easy way in rails to do this?
Greets,
Nico
Your question isn't really a Rails question, it's a general Ruby programming question.
Your description isn't very clear, but from what I understand, you want to group IDs for common categories using a Hash. There are various other ways of doing this, but this is easy to understand::
ary = [
'cat1', {:id => 1},
'cat2', {:id => 2},
'cat1', {:id => 3}
]
hsh = {}
ary.each_slice(2) { |a|
key,category = a
hsh[key] ? hsh[key] << category[:id] : hsh[key] = [category[:id]]
}
hsh # => {"cat1"=>[1, 3], "cat2"=>[2]}
I'm using a simple Array with a category, followed by a simple hash representing some object instance, because it makes it easy to visualize. If you have a more complex object, replace the hash entries with those objects, and tweak how you access the ID in the ternary (?:) line.
Using Enumerable.inject():
hsh = ary.each_slice(2).inject({}) { |h,a|
key,category = a
h[key] ? h[key] << category[:id] : h[key] = [category[:id]]
h
}
hsh # => {"cat1"=>[1, 3], "cat2"=>[2]}
Enumerable.group_by() could probably shrink it even more, but my brain is fading.
I'd use Enumerable#inject
content_categories = content_categories_array.inject({}){ |memo, category| memo[category] = Category.get_contents(category); memo }
Hash[content_categories.map{|cat|
[cat, Category.get_contents(cat)]
}]
Not really the right answer, because you want IDs in your array, but I post it anyway, because it's nice and short, and you might actually get away with it:
content_categories.group_by(&:category)
content_categories.each do |k,v|
content_categories[k] = Category.getContents(v)
end
I suppose it's works
If i understand correctly, content_categories is an array of categories, which needs to be turned into a hash of categories, and their elements.
content_categories_array = content_categories
content_categories_hash = {}
content_categories_array.each do |category|
content_categories_hash[category] = Category.get_contents(category)
end
content_categories = content_categories_hash
That is the long version, which you can also write like
content_categories = {}.tap do |hash|
content_categories.each { |category| hash[category] = Category.get_contents(category) }
end
For this solution, content_categories must be a hash, not an array as you describe. Otherwise not sure where you're getting the key.
contents_by_categories = Hash[*content_categories.map{|k, v| [k, Category.getContents(v.id)]}]

Resources