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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.