Reputation: 2494
I need an hint to convert four arrays into an hash.
My post params has these four arrays:
"jcrop-x"=>["0", "614", "0", "798"],
"jcrop-y"=>["0", "0", "273", "286"],
"jcrop-x2"=>["717", "666", "678", "482"],
"jcrop-y2"=>["567", "563", "529", "516"],
This is a sort of matrix: The value with index 0 of every array is the crop coordinates for my main_image
. The second value is the crop coordinates for my square_image
, etc.
I need to populate an array with this structure:
crop_params{
main_image: {x: 0, y: 0, x2: 717, y2: 567},
second_image: {x: 614, y: 0, x2: 666, y2: 563},
third_image: {x: 0, y: 273, x2: 678, y2: 529},
fourth_image: {x: 798, y: 286, x2: 482, y2: 516}
}
This is my actual solution:
IMAGE_VERSION = [:main_news_img, :square_img, :vertical_img, :horizontal_img]
crop_params = {}
IMAGE_VERSION.each_with_index do |v,i|
crop_params[v] = {}
crop_params[v]["x"] = params['content']["jcrop-x"][i]
crop_params[v]["y"] = params['content']["jcrop-y"][i]
crop_params[v]["x2"] = params['content']["jcrop-x2"][i]
crop_params[v]["y2"] = params['content']["jcrop-y2"][i]
end
I can do that with many lines of code. Is there any smart way to keep my helper clean and readable?
Upvotes: 1
Views: 110
Reputation: 110675
I would do it this way:
params = {
"jcrop-x" => [ "0", "614", "0", "798"],
"jcrop-y" => [ "0", "0", "273", "286"],
"jcrop-x2" => ["717", "666", "678", "482"],
"jcrop-y2" => ["567", "563", "529", "516"],
}
IMAGE_VERSIONS = [:main_news_img, :square_img, :vertical_img, :horizontal_img]
def map_params(params)
v = params.map { |key, values| [key.split('-').last, values]}
# [["x", [ "0", "614", "0", "798"]],["y" ,[ "0", "0", "273", "286"]],
# ["x2",["717", "666", "678", "482"]],["y2",["567", "563", "529", "516"]]]
keys, values = [v.map(&:first).map(&:to_sym), v.map(&:last).transpose]
# keys => [:x, :y, :x2, :y2]
# values => [["0", "0", "717", "567"], ["614", "0", "666", "563"],
# ["0", "273", "678", "529"], ["798", "286", "482", "516"]]
IMAGE_VERSIONS.each_with_object({}) {|k,h| h[k]=Hash[keys.zip(values.shift)]}
end
# { :main_news_img => {:x=> "0", :y=> "0", :x2=>"717", :y2=>"567"},
# :square_img => {:x=>"614", :y=> "0", :x2=>"666", :y2=>"563"},
# :vertical_img => {:x=> "0", :y=>"273", :x2=>"678", :y2=>"529"},
# :horizontal_img => {:x=>"798", :y=>"286", :x2=>"482", :y2=>"516"} }
If the keys :x, :y, :x2, :y2
are not to be determined from the data, add
KEYS = [:x, :y, :x2, :y2]
and simplify the method to:
def map_params(params)
values = params.map { |_, values| values }.transpose
# [["0", "0", "717", "567"], ["614", "0", "666", "563"],
# ["0", "273", "678", "529"], ["798", "286", "482", "516"]]
IMAGE_VERSIONS.each_with_object({}) {|k,h|h[k]=Hash[KEYS.zip(values.shift)]}
end
Upvotes: 0
Reputation: 160551
Starting with an incoming set of parameters like:
params = {
"jcrop-x" => ["0", "614", "0", "798"],
"jcrop-y" => ["0", "0", "273", "286"],
"jcrop-x2" => ["717", "666", "678", "482"],
"jcrop-y2" => ["567", "563", "529", "516"],
}
I'd use:
transposed_params_values = params.values.transpose
crop_params = Hash[
*[:main_image, :second_image, :third_image, :fourth_image].flat_map { |k|
[
k,
Hash[
[:x, :y, :x2, :y2].zip(transposed_params_values.shift)
]
]
}
]
# => {:main_image=>{:x=>"0", :y=>"0", :x2=>"717", :y2=>"567"}, :second_image=>{:x=>"614", :y=>"0", :x2=>"666", :y2=>"563"}, :third_image=>{:x=>"0", :y=>"273", :x2=>"678", :y2=>"529"}, :fourth_image=>{:x=>"798", :y=>"286", :x2=>"482", :y2=>"516"}}
Or, written a bit more compactly:
transposed_params_values = params.values.transpose
crop_params = Hash[
*[:main_image, :second_image, :third_image, :fourth_image].flat_map { |k|
[k, Hash[ [:x, :y, :x2, :y2].zip(transposed_params_values.shift) ]]
}
]
# => {:main_image=>{:x=>"0", :y=>"0", :x2=>"717", :y2=>"567"}, :second_image=>{:x=>"614", :y=>"0", :x2=>"666", :y2=>"563"}, :third_image=>{:x=>"0", :y=>"273", :x2=>"678", :y2=>"529"}, :fourth_image=>{:x=>"798", :y=>"286", :x2=>"482", :y2=>"516"}}
Here's a breakdown of what's happening:
transposed_params_values = params.values.transpose
# => [["0", "0", "717", "567"], ["614", "0", "666", "563"], ["0", "273", "678", "529"], ["798", "286", "482", "516"]]
Hash[[:x, :y, :x2, :y2].zip(transposed_params_values.first)]
# => {:x=>"0", :y=>"0", :x2=>"717", :y2=>"567"}
Upvotes: 0
Reputation: 107989
IMAGE_VERSIONS = [:main_news_img, :square_img, :vertical_img, :horizontal_img]
def map_params(params)
hashes = params.map do |key, values|
values.map do |value|
[key.split('-').last, value.to_i]
end
end.transpose.map do |key_value_pairs|
Hash[key_value_pairs]
end
Hash[IMAGE_VERSIONS.zip(hashes)]
end
pp map_params(params)
# => {:main_news_img=>{"x"=>0, "y"=>0, "x2"=>717, "y2"=>567},
# => :square_img=>{"x"=>614, "y"=>0, "x2"=>666, "y2"=>563},
# => :vertical_img=>{"x"=>0, "y"=>273, "x2"=>678, "y2"=>529},
# => :horizontal_img=>{"x"=>798, "y"=>286, "x2"=>482, "y2"=>516}}
Upvotes: 1
Reputation: 121
A version using zipping and mapping to achieve what you're looking for:
require 'pp'
params = {
"jcrop-x"=>["0", "614", "0", "798"],
"jcrop-y"=>["0", "0", "273", "286"],
"jcrop-x2"=>["717", "666", "678", "482"],
"jcrop-y2"=>["567", "563", "529", "516"]
}
# 1. Extract the value arrays, mapping all values to the result
# of calling .to_i on them:
in_keys = %w{jcrop-x jcrop-y jcrop-x2 jcrop-y2}
in_values = in_keys.map { |key| params[key].map(&:to_i) }
# 2. Now transpose the array of arrays:
in_values_t = in_values.transpose
# 3. Now we have an array of arrays of numbers. We want this to be
# an array of hashes instead, so we map them:
out_subkeys = %i{x y x2 y2}
out_values = in_values_t.map { |a| Hash[out_subkeys.zip(a)] }
# 4. Finally, we simply zip that array with the output keys:
out_keys = %i{main_image second_image third_image fourth_image}
crop_params = Hash[out_keys.zip(out_values)]
pp crop_params
Upvotes: 0
Reputation: 48348
Hmm, well I'd say as is your solution is pretty good. Here's another way you might do it:
params = {
"content" => {
"jcrop-x"=>["0", "614", "0", "798"],
"jcrop-y"=>["0", "0", "273", "286"],
"jcrop-x2"=>["717", "666", "678", "482"],
"jcrop-y2"=>["567", "563", "529", "516"],
}
}
IMAGE_VERSION = [:main_news_img, :square_img, :vertical_img, :horizontal_img]
IMAGE_VERSION.each_with_index.with_object({}) do |image_and_index, object|
image_version, i = image_and_index
object[image_version] = Hash[params['content'].map{|key, value| [key.gsub(/^jcrop-/, ''), value[i]]}]
end
Upvotes: 1
Reputation: 44685
Assuming you always have 4 images try:
Hash[[:main_image, :second_image, :third_image, :fourth_image].zip params.values.transpose.map{|a| Hash[[:x, :y, :x2, :y2].zip(a)]}]
If not it will need to be modified slightly.
Upvotes: 1