Adding increasing key to Hash - ruby

PHP allows to add values to array like this:
array[]='a' # result: arr[0]='a'
array[]='a' # result: arr[1]='a'
...
How to achieve similar result with Ruby?
UPDATED:
forgot to say, that I need to make some extra hashes inside, like
'a'=>{1=>1}...
UPDATE 2:
First update can be a little bit confusing, so there is my full source, which doesn't work. It has to make multiple records of #value hash in session[:items][0], session[:items][1]...
#value = {'id'=>id, 'quantity'=>1, "property_categories"=>params[:property_categories]}
if !session[:items].present?
session[:items] = #value
else
session[:items].push(#value)
end
UPDATE 3:
data should look like:
[0=>{'id'=>id, 'quantity'=>1, "property_categories"=>{1=>1}},
1=>{'id'=>id, 'quantity'=>1, "property_categories"=>{1=>1}}]...

this should work:
arr << 'a'
or this:
arr.push('a')
source: http://www.ruby-doc.org/core-2.1.1/Array.html

In your code, if session[:items] is not present, you are assigning #value(which is a Hash) to it. So next time it will try to push items to Hash.
If you need an Array for session[:items], this should work
if !session[:items].present?
session[:items] = [#value] # Here create an array with #value in it
else
session[:items].push(#value)
end
EDIT
I see you have updated your question. So here is my updated answer
if !session[:items].present?
session[:items] = { 0 => #value }
else
session[:items][ session[:items].keys.max + 1 ] = #value
end

Simply push to the array using <<
array = []
array << 'a' # array == ["a"]
array << 'a' # array == ["a", "a"]

Related

How to remove duplicate pair values from given array in Ruby?

I want to remove a pair of 'duplicates' from an array of strings, where each element has the form R1,R2, with varying numbers. In my case, a duplicate would be R2,R1 because it has the same elements of R1,R2 but inverted.
Given:
a = ['R1,R2', 'R3,R4', 'R2,R1', 'R5,R6']
The resulting array should be like so:
a = ['R1,R2', 'R3,R4', 'R5,R6']
How could I remove the duplicates so I would have the following?
A solution with Set
require 'set'
a.uniq { |item| Set.new(item.split(",")) } # => ["R1,R2", "R3,R4", "R5,R6"]
Here is a working example :
array = ['R1,R2', 'R3,R4', 'R2,R1', 'R5,R6']
array.uniq { |a| a.split(',').sort }
try this,
def unique(array)
pure = Array.new
for i in array
flag = false
for j in pure
flag = true if (j.split(",").sort == i.split(",").sort)
end
pure << i unless flag
end
return pure
end
reference: https://www.rosettacode.org/wiki/Remove_duplicate_elements#Ruby
If the elements of your array are "pairs", they should maybe be actual pairs and not strings, like this:
pairs = [['R1', 'R2'], ['R3', 'R4'], ['R2', 'R1'], ['R5', 'R6']]
And, in fact, since order doesn't seem to matter, it looks like they really should be sets:
require 'set'
sets = [Set['R1', 'R2'], Set['R3', 'R4'], Set['R2', 'R1'], Set['R5', 'R6']]
If that is the case, then Array#uniq will simply work as expected:
sets.uniq
#=> [#<Set: {"R1", "R2"}>, #<Set: {"R3", "R4"}>, #<Set: {"R5", "R6"}>]
So, the best way would be to change the code that produces this value to return an array of two-element sets.
If that is not possible, then you should transform the value at your system boundary when it enters the system, something like this:
sets = a.map {|el| el.split(',') }.map(&Set.method(:new))

Ruby array initialize on read

I would like to be able to do this:
my_array = Array.new
my_array[12] += 1
In other words, somehow upon trying to access entry 12, finding it uninitialized, it is initialized to zero so I can add one to it. Array.new has a default: parameter, but that comes into play when you initialize the array with a known number of slots. Other than writing my own class, is there a ruby-ish way of doing this?
No need to create a new class :
my_hash = Hash.new(0)
my_hash[12] += 1
p my_hash
#=> {12=>1}
For many cases, hashes and arrays can be used interchangeably.
An array with an arbitrary number of elements and a default value sounds like a hash to me ;)
Just to make it clear : Hash and Array aren't equivalent. There will be cases where using a hash instead of an array will be completely wrong.
Something like:
a[12] = (a[12] ||= 0) + 1
Making use of nil.to_i == 0
my_array = Array.new
my_array[12] = my_array[12].to_i + 1
Note, that unlike other solutions here so far, this one works for any arbitrary initial value.
my_array = Array.new.extend(Module.new {
def [] idx
super || 0
end
})
my_array[12] += 1
#⇒ 1
This is not possible with the stock Array::new method.
https://docs.ruby-lang.org/en/2.0.0/Array.html#method-c-new
You will either need to monkey patch Array class, or monkey patch nil class. And they are not recommended.
If you have a specific use case, I would create a new wrapper class around Array
class MyArray < Array
def [](i)
super(i) ? super(i) : self[i] = 0
end
end
arr = MyArray.new
arr[12] += 1 # => 1

Ruby-How to build a multivalued hash?

Here is my code snippet:
something_1.each do |i|
something_2.each do |j|
Data.each do |data|
date = data.attribute('TIME_PERIOD').text
value = data.attribute('OBS_VALUE').text
date_value_hash[date] = value
end
end
end
I want to capture all the values in a single date. date is the key of my hash and it may have multiple values for a single date. How can I accomplish that here? When I am using this line:
date_value_hash[date] = value
values are getting replaced each time the loop iterates. But, I want to accumulate all the values in my date_value_hash for each dates i.e. I want to build the values dynamically.
Currently I am getting this:
{"1990"=>"1", "1994"=>"2", "1998"=>"0"}
But, I want something like this:
{"1990"=>"1,2,3,4,5,6", "1994"=>"1,2,3,4,5,6", "1998"=>"1,2,3,4,5,6"}
Anyone have any idea how can I accomplish that?
Like this
magic = Hash.new{|h,k|h[k]=[]}
magic["1990"] << "A"
magic["1990"] << "B"
magic["1994"] << "C"
magic["1998"] << "D"
magic["1994"] << "F"
after which magic is
{"1998"=>["D"], "1994"=>["C", "F"], "1990"=>["A", "B"]}
and if you need the values as comma separated string (as indicated by your sample data), you'll just access them as
magic['1990'].join(',')
which yields
"A,B"
if later you want to pass magic around and preventing it from automagically creating keys, just wrap it as follows
hash = Hash.new.update(magic)
Hope that helps!
Another approach of building multi-valued hash in Ruby:
h = {}
(h[:key] ||= []) << "value 1"
(h[:key] ||= []) << "value 2"
puts h

How i can sort this hash by created of and key ,value pair

Hi i am little struggle to make this hash and sort it by created of and key , value pair.
Here is my code
hash_answers = {}
unless answers.blank?
answers.each_with_index do |ans ,index|
voted_up_users = ans.votes_up_by_all_users(ans)
voted_down_users = ans.votes_down_by_all_users(ans)
hash_answers[ans.id] = voted_up_users.count -voted_down_users.count #line one
hash_answers[index+1] = ans.created_at # line 2
end
end
if i have line 1 only in code not line 2 then this below code work fine for me
#answers = hash_answers.sort_by { |key, value| value }.reverse
but i also want to sort it by craeted_at
How i can achive this or make hash in another way
Any help will be most appreciated
Thanks
answers.sort_by do |ans|
[ans.net_votes, ans.created_at]
end
Then in your Answers class
def net_votes
votes_up_by_all_users - votes_down_by_all_users
end
You shouldn't have to pass an object to itself as a variable as in ans.votes_up_by_all_users(ans). Objects always know about themselves.
Usually you can sort on a number of things by creating an array of these things and use that as your sort key:
#answers = hash_answers.sort_by { |k, v| [ v[:created_at], v[:count] }
This is dependent on having a sortable structure to start with. You're jamming two entirely different things into the same hash. A better approach might be:
hash_answers[ans.id] = {
:id => ans.id,
:count => voted_up_users.count -voted_down_users.count,
:created_at => ans.created_at
}
You can adjust the order of the elements in the array to sort in the correct order.

Remove the '-' dividers in JSON keys in Ruby

I'm trying to read some JSON data from the Tumblr API.
I'm using the Hashie gem to read the values as object properties. This should make reading easier/cleaner.
it turns something like this:
data['post']['title']
into this:
data.post.title
Unfortunately there are some keys showing up with a '-' as divider between like this:
regular-title: Mijn eerste post
format: html
regular-body: <p>post</p>
therefore i cannot use post.regular-title. Is there a way to replace all the minus(-) symbols into underscores(_)?
This will do it:
def convert_object(data)
case data
when Hash
data.inject({}) do |h,(k,v)|
h[(k.respond_to?(:tr) ? k.tr('-', '_') : k)] = convert_object(v)
h
end
when Array
data.map { |i| convert_object(i) }
else
data
end
end
You can use it like this:
convert_object(JSON.parse('{"something-here":"value","otherkey":{"other-key":"value-value"}}'))
Karaszi Istvan helped me a lot with the solution. I added the check for an array in the hash. This way hashes in arrays in the hash will get underscored too.
def convert_hash(hash)
case hash
when Hash
hash.inject({}) do |h,(k,v)|
h[k.tr('-', '_')] = convert_hash(v)
h
end
when Array
array = hash
number = 0
array.each do
array[number] = convert_hash(array[number])
number += 1
end
array
else
hash
end
end
I don't know why i added the 'number' as iterator. Somehow hash.each didn't work.

Resources