1

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.

3
  • 1
    Have a look at this question for a generic implementation of a counted set. Commented Jan 22, 2014 at 2:34
  • some tests would avoid the confusion :) Commented Jan 22, 2014 at 2:51
  • i've added a go at it as i think you've explained it - tests would be useful that's true :-) Commented Jan 22, 2014 at 4:58

4 Answers 4

1

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 }
Sign up to request clarification or add additional context in comments.

Comments

1

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

Comments

0

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

2 Comments

This is close. I meant when data.s and data.t already exists in the @final array for an existing object. In this case, I would like to simply increase the count.
I just updated my post to be more clear. Sorry for the confusion.
0
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)

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.