ruby - Inserting multiple hashes into an array in ruby - arrays

I want to insert multiple hashes into an array which will create an array of hashes. But every time I add a new hash to the array, it would overwrite the previous ones. Any idea what is going on?
partArray = []
partHash = {}
partHash["name"] = "Item1"
partHash["owner"] = "Item1"
#Insert first hash into array
partArray << partHash
puts partArray
#new set of key, value pairs
#to be appended to array
partHash["name"] = "Item2"
partHash["owner"] = "Item2"
#Append second hash into array
partArray << partHash
puts partArray
output :
{"name"=>"Item1", "owner"=>"Item1"}
new Array is :
{"name"=>"Item2", "owner"=>"Item2"}
{"name"=>"Item2", "owner"=>"Item2"}
I'm not sure why the values in the first hash were overwritten. Any help is appreciated.

You're saving the same hash in two different locations of the array. Think of Ruby as adding object references instead of copying the object each time you nest it inside another thing.
Do do this with different values, you might want to create a new one each time:
part_array = [ ]
part_array << {
# ... Hash entry
}
There are legitimate reasons why you might want to put the same thing in an array twice, it could be a way of saving memory when using a large data structure.
As a note, Ruby tends to strongly recommend variable names like part_array, all lower-case. Likewise, for Hash keys, Symbols are often preferred since they're inexpensive internally.

I'm not sure why the values in the first hash were overwritten?
Firstly we define an empty array and an empty hash.
partArray = []
partHash = {}
Now we create two new key-value pairs in our hash. Because these keys don't currently exist within partHash, they are created for you.
partHash["name"] = "Item1"
partHash["owner"] = "Item1"
parthHash #{"name"=>"Item1", "owner"=>"Item1"}
Insert our hash into our array:
partArray << partHash
partArray #[{"name"=>"Item1", "owner"=>"Item1"}]
Here is the key step. Because the keys "name" and "owner" already exist within the hash, the []= notation will simply redefine any existing values, so.
partHash["name"] = "Item2"
partHash["owner"] = "Item2"
partHash # {"name"=>"Item2", "owner"=>"Item2"}
partArray #[{"name"=>"Item2", "owner"=>"Item2"}]
Currently partArray contains a single partsHash hash. Finally you append partHash into partArray again:
partArray << partHash
partArray #[{"name"=>"Item2", "owner"=>"Item2"}, {"name"=>"Item2", "owner"=>"Item2"}]

Related

In Ruby, during iteration of an array, how do I send multiple array elements as values for one specific hash key?

I know how to iterate through an array and I know how to create hash keys and values. I do not know how to create an array for the value and pass it multiple elements. My desired value for hash below is:
{'abc' => [1, 2, 3] , 'def' => [4,5,6,7]}
How would I achieve this hash, while iterating through array a and b below using each?
a = [1,2,3,4,5,6,7]
c = [1,2,3]
b = ['abc', 'def']
hash = {}
From your guidelines given in the comment:
While iterating through array a, if the element of iteration is included in array c, it is passed to the array value within key 'abc'. Otherwise, it is passed to other array value in key 'def'
You can do this:
hash = {}
hash['abc'] = a.select { |x| c.include?(x) }
hash['def'] = a.reject{ |x| c.include?(x) }
See Enumerable#select and Enumerable#reject. Also can take a look at Enumerable#partition which would be another good choice here, where you want to split an array into two arrays based on some condition:
in_a, not_in_a = a.partition { |x| c.include?(x) }
hash = { 'abc' => in_a, 'def' => not_in_a }
You can also do it with regular each if these fancy enumerable methods are bit too much for you:
hash = { 'abc' => [], 'def' => [] }
a.each do |x|
if c.include?(x)
hash['abc'].push(x)
else
hash['def'].push(x)
end
end
Unfortunately this question turned out not to be as interesting as I was hoping. I was hoping that the problem was this:
Knowing the hash key and a value, how can I make sure the key's value is an array and that the given value is appended to that?
For instance, start with h being {}. I have a key name :k and a value 1. I want h[:k], if it doesn't already exist, to be [1]. But if it does already exist, then it's an array and I want to append 1 to that array; for instance, if h[:k] is already [3,2], now it should be [3,2,1].
I can think of various ways to ensure that, but one possibility is this:
(hash[key] ||= []) << value
To see that this works, let's make it a method:
def add_to_hash_array_value(hash:, key:, value:)
(hash[key] ||= []) << value
end
Now I'll just call that a bunch of times:
h = {}
add_to_hash_array_value(hash:h, key:"abc", value:1)
add_to_hash_array_value(hash:h, key:"abc", value:2)
add_to_hash_array_value(hash:h, key:"def", value:4)
add_to_hash_array_value(hash:h, key:"def", value:5)
add_to_hash_array_value(hash:h, key:"abc", value:3)
puts h #=> {"abc"=>[1, 2, 3], "def"=>[4, 5]}
We got the right answer.
This is nice because suppose I have some way of knowing, given a value, what key it should be appended to. I can just repeatedly apply that decision-making process.
However, the trouble with trying to apply that to the original question is that the original question seems not to know exactly how to decide, given a value, what key it should be appended to.

How can I remove an item in a Ruby array based on its content?

I'm making a sort of variable system in Ruby where a user can create a variable. Provided they give a variable name and contents, it adds the name and string given to an array, adding two more items to the array. Here's the code:
$vars = []
def create(name, value)
$vars << name
$vars << value
end
I also want to add a "delete" option where the user types delete("varName") and behind the scenes, it searches for an item with the same value in the $vars array and deletes it. Is there any way to do this? Also, is there any way to get the number of the item (e.g. items in the array are ["a", "b", "c", "d"] and the user wants to delete "d",and then the program returns 3, since that's the location of "d".
A Hash is used to store pairs of items. It is faster, and guarantees names will be unique.
$vars = {}
def create(name, value)
$vars[name] = value
end
Then deleting is trivial.
$vars.delete(name)
As is finding the value.
value = $vars[name]
Hashes remember the order in which keys were added. $vars.keys will return an Array of the names in the order they were added. Then you can use find_index to get where it appears in the list of keys.
index = $vars.keys.find_index(name)
Aside from letting the user know the order in which variables are declared, this isn't of much use. Variables don't have an order.
If you wanted to do this with arrays, first we'd fix create. It's storing both the key and the value in the same Array.
create(a, 23)
create(b, 42)
# $var = ['a', 23, 'b', 42]
Instead, store each pair in its own array.
def create(name, value)
$vars << [name, value]
end
create(a, 23)
create(b, 42)
# $var = [['a', 23], ['b', 42]]
Then to delete, search just the first elements of each pair using index. Then delete that index.
def delete(name)
idx = $vars.map(&:first).index(name)
$vars.delete_at(idx) if idx
return idx
end
Finding the value of a name would work similarly. Find the index of the matching name, look up that pair, return its second element (the value).
def find(name)
idx = $vars.map(&:first).index(name)
pair = $vars[idx]
return pair[1]
end
But don't do this, use a Hash.
$vars = ["name", "value", "item"]
index = $vars.index("name")
$vars.delete_at(index)
Sources:
Duplicate?
Editor
Documentation
Although if this is rails, is it really a good idea to use a GLOBAL variable?

How to collapse a multi-dimensional array of hashes in Ruby?

Background:
Hey all, I am experimenting with external APIs and am trying to pull in all of the followers of a User from a site and apply some sorting.
I have refactored a lot of the code, HOWEVER, there is one part that is giving me a really tough time. I am convinced there is an easier way to implement this than what I have included and would be really grateful on any tips to do this in a much more eloquent way.
My goal is simple. I want to collapse an array of arrays of hashes (I hope that is the correct way to explain it) into one array of hashes.
Problem Description:
I have an array named f_collectionswhich has 5 elements. Each element is an array of size 200. Each sub-element of these arrays is a hash of about 10 key-value pairs. My best representation of this is as follows:
f_collections = [ collection1, collection2, ..., collection5 ]
collection1 = [ hash1, hash2, ..., hash200]
hash1 = { user_id: 1, user_name: "bob", ...}
I am trying to collapse this multi-dimensional array into one array of hashes. Since there are five collection arrays, this means the results array would have 1000 elements - all of which would be hashes.
followers = [hash1, hash2, ..., hash1000]
Code (i.e. my attempt which I do not want to keep):
I have gotten this to work with a very ugly piece of code (see below), with nested if statements, blocks, for loops, etc... This thing is a nightmare to read and I have tried my hardest to research ways to do this in a simpler way, I just cannot figure out how. I have tried flatten but it doesn't seem to work.
I am mostly just including this code to show I have tried very hard to solve this problem, and while yes I solved it, there must be a better way!
Note: I have simplified some variables to integers in the code below to make it more readable.
for n in 1..5 do
if n < 5
(0..199).each do |j|
if n == 1
nj = j
else
nj = (n - 1) * 200 + j
end
#followers[nj] = #f_collections[n-1].collection[j]
end
else
(0..199).each do |jj|
njj = (4) * 200 + jj
#followers[njj] = #f_collections[n-1].collection[jj]
end
end
end
Oh... so It is not an array objects that hold collections of hashes. Kind of. Lets give it another try:
flat = f_collection.map do |col|
col.collection
end.flatten
which can be shortened (and is more performant) to:
flat = f_collection.flat_map do |col|
col.collection
end
This works because the items in the f_collection array are objects that have a collection attribute, which in turn is an array.
So it is "array of things that have an array that contains hashes"
Old Answer follows below. I leave it here for documentation purpose. It was based on the assumption that the data structure is an array of array of hashes.
Just use #flatten (or #flatten! if you want this to be "inline")
flat = f_collections.flatten
Example
sub1 = [{a: 1}, {a: 2}]
sub2 = [{a: 3}, {a: 4}]
collection = [sub1, sub2]
flat = collection.flatten # returns a new collection
puts flat #> [{:a=>1}, {:a=>2}, {:a=>3}, {:a=>4}]
# or use the "inplace"/"destructive" version
collection.flatten! # modifies existing collection
puts collection #> [{:a=>1}, {:a=>2}, {:a=>3}, {:a=>4}]
Some recommendations for your existing code:
Do not use for n in 1..5, use Ruby-Style enumeration:
["some", "values"].each do |value|
puts value
end
Like this you do not need to hardcode the length (5) of the array (did not realize you removed the variables that specify these magic numbers). If you you want to detect the last iteration you can use each_with_index:
a = ["some", "home", "rome"]
a.each_with_index do |value, index|
if index == a.length - 1
puts "Last value is #{value}"
else
puts "Values before last: #{value}"
end
end
While #flatten will solve your problem you might want to see how DIY-solution could look like:
def flatten_recursive(collection, target = [])
collection.each do |item|
if item.is_a?(Array)
flatten_recursive(item, target)
else
target << item
end
end
target
end
Or an iterative solution (that is limited to two levels):
def flatten_iterative(collection)
target = []
collection.each do |sub|
sub.each do |item|
target << item
end
end
target
end

How to efficiently get an array of values from a referenced hash and an array of keys in perl?

I currently have an referenced hash and an array of keys that the hash contains. I want to get an array of the values corresponding to my array of keys.
I know how to do this in multiple lines:
# Getting hash reference and array of keys.
my $hashRef = {
one => 'foo',
two => 'bar',
three => 'baz'
};
my #keys = ('one', 'three');
# Getting corresponding array of values.
my #values;
foreach my $key (#keys) {
push #values, $hashRef->{$key};
}
However, I believe that there must be a much better way that doesn't make use of a loop. But unfortunately I just can't figure it out. How can I efficiently
get an array of values from a referenced hash and an array of keys; ideally in one line if possible?
Easily:
my #values = #$hashRef{#keys};
Or, on Perl 5.24+:
my #values = $hashRef->#{#keys};
Or, on Perl 5.20+ by enabling some additional features:
use feature qw(postderef);
no warnings qw(experimental::postderef);
my #values = $hashRef->#{#keys};
This takes advantage of the fact that you can get the values for multiple keys (a "slice") of a %hash with the #hash{LIST} syntax. You just have to dereference it first. See perldoc for more information.

Ruby: Group array of array of object

I would like to group an array of arrays of objects by object attribute.
So, if we have
array =[[object1, object2], [object3], [object4, object5, object6]]
and this object has an attribute say "source". Now assume each object has the following source value:
object1> source=source1
object2> source=source2
object3> source=source2
object4> source=source1
object5> source=source1
object6> source=source3
I want to group the arrays that are inside "array" by source attribute.
so I would like to have:
source1 => [object1, object2], [object4, object5, object6]
source2 => [object1, object2]
source3 => [object4, object5, object6]
for source2, I don't care about array of size 1 so I don't want to add [object3]
Any help?
The key thing, which became apparent after your edit, is that you have already grouped your objects, and you want to bucket your groups into potentially more than one bucket based on the objects in each grouping, so this isn't a pure group_by algorithm. Here is one possible solution:
First, create a hash to hold your groups. This sets an empty array as the default value:
sources = Hash.new {|h,k| h[k] = Array.new }
Then reject the ones you don't care about (see Enumerable#reject).
array.reject{|group| group.size == 1}
Finally, iterate through the groups, collecting their sources and adding the group to the appropriate source in the hash. Full example:
sources = Hash.new {|h,k| h[k] = Array.new }
array.reject{|group| group.size == 1}.each do |group|
group.collect{|group| group.source}.uniq.each do |source|
sources[source] << group
end
end

Resources