Reputation: 4038
I am creating a list of hashes in an array and would like to keep a count if they are the same.
Here is what an example hash looks like:
data = {
s: y.id,
t: z.id,
count: 0
}
I am iterating through a bunch of these hashes and pushing them onto a list. I would like it so that when the values for s
and t
already exist in a hash in the list, the count
would be incremented.
Let me clarify. Suppose this is my @list
@list = [
{
s: 1,
t: 2,
count: 5
},
{
s: 1,
t: 3,
count: 5
}
]
Now suppose, I want to push the following hash to the list:
data = {
s: 1,
t: 2,
count: 0
}
The result of @list should look like this because the hash with s==1
and t==2
already exists in the list:
@list = [
{
s: 1,
t: 2,
count: 6
},
{
s: 1,
t: 3,
count: 5
}
]
This is where I am currently.
@final = []
while widgets.count > 1
widget = widgets.shift
widgets.each do |w|
data = {
s: widget.id,
t: w.id,
count: 0
}
@final << data
end
end
This simply adds all the permutations to the list but I want to prevent the dups when s
and t
are identical and simply increment count
.
I hope I am clear.
Any suggestions would be greatly appreciated.
Upvotes: 1
Views: 516
Reputation: 110675
I'd do it like this (assuming I understand the question correctly):
def add_hash(data)
h, i = @list.each_with_index.find {|h,i| data[:s]==h[:s] && data[:t]==h[:t]}
if h
@list[i][:count] += 1
else
data[:count] = 1
@list << data
end
end
add_hash( { s: 1, t: 3, count: 0 } )
@list # @list => [{:s=>1, :t=>2, :count=>5}, {:s=>1, :t=>3, :count=>6}]
add_hash( { s: 2, t: 3, count: 0 } )
@list # @list # => [{:s=>1, :t=>2, :count=>5}, {:s=>1, :t=>3, :count=>5},
{:s=>2, :t=>3, :count=>1}]
If you can change @list
, consider making it a hash:
@hash = { { s: 1, t: 2 } => 5, { s: 1, t: 3 } => 5 }
Upvotes: 1
Reputation: 8202
def reduce_matches(collection)
result.reduce([]) do |arr, element|
matching(arr, element) ? matching[:count] += 1 : arr << element
arr
end
end
def matching(coll, candidate)
coll.detect do |element|
element[:s] == candidate[:s] && element[:t] == candidate[:t]
end
end
Now you can type:
reduce_matches(widgets)
which gives you what you need. For example if
widgets = [
{
s: 1,
t: 2,
count: 0
},
{
s: 2,
t: 3,
count: 0
},
{
s: 1,
t: 2,
count: 0
},
]
then
reduce_matches(widgets) = [
{
s: 1,
t: 2,
count: 1
},
{
s: 2,
t: 3,
count: 0
}
]
Want to add a new element to widgets?
widgets << new_element
reduce_matches(widgets)
Upvotes: 0
Reputation: 1566
If I get your question right you could use the find method in list passing a block where you specify the conditions you want to match (that values of s and t are already present in the @final list).
This is an example where I use lists and hashes directly.
widgets = [{s:1, t:2, count:0}, {s: 1, t:2, count:0}, {s: 1, t:2, count:0},
{s:1, t:3, count:0}, {s:1, t:3, count:0}]
@final = []
widgets.each do |widget|
res = @final.find {|obj| obj[:s] == widget[:s] && obj[:t] == widget[:t] }
if res.nil?
widget[:count] = 1
@final << widget
else
res[:count] += 1
end
end
puts @final.inspect
And the answer from this code is
[{:s=>1, :t=>2, :count=>3}, {:s=>1, :t=>3, :count=>2}]
as expected
Upvotes: 1
Reputation: 3537
Not sure whether I am intepreting your question correctly, but if you want the count
attribute in each data
hash to be incremented when data.s == data.t
, this should do the trick:
@final = []
while widgets.count > 1
widget = widgets.shift
widgets.each do |w|
data = {
s: widget.id,
t: w.id,
count: 0
}
if data.s == data.t
data.count += 1
end
@final << data
end
end
Upvotes: 0