Isolating non-unique items in an array

J

Jason Burgett

I'm basically trying to the opposite of .uniq Let's say I have an array:

["a", "a", "a", "b", "c", "d", "d"]

I would like to boil this down by first tossing the values that only
appear once. Which leaves me with:

["a", "a", "a", "d", "d"]

Then I need to somehow determine that "a" appears 3 times and "d"
appears 2 times. Any help would be great. Thanks.
 
T

Tim Pease

I'm basically trying to the opposite of .uniq Let's say I have an array:

["a", "a", "a", "b", "c", "d", "d"]

I would like to boil this down by first tossing the values that only
appear once. Which leaves me with:

["a", "a", "a", "d", "d"]

Then I need to somehow determine that "a" appears 3 times and "d"
appears 2 times. Any help would be great. Thanks.

class Array
def count( item )
return 0 unless self.include? item

c = 0
self.each {|i| c += 1 if i == item}
return c
end
end

ary = %w(a a a b c d d)
new_ary = ary.select {|item| ary.count(item) > 1}

counts = Hash.new {|h,k| h[k] = new_ary.count(k)}


It's not going to be fast, but it should work.

Blessings,
TwP
 
A

Alex Young

Tim said:
I'm basically trying to the opposite of .uniq Let's say I have an array:

["a", "a", "a", "b", "c", "d", "d"]

I would like to boil this down by first tossing the values that only
appear once. Which leaves me with:

["a", "a", "a", "d", "d"]

Then I need to somehow determine that "a" appears 3 times and "d"
appears 2 times. Any help would be great. Thanks.

class Array
def count( item )
return 0 unless self.include? item

c = 0
self.each {|i| c += 1 if i == item}
return c
end
end

ary = %w(a a a b c d d)
new_ary = ary.select {|item| ary.count(item) > 1}

Better:

class Array
def counts # could be golfed, but clarity is king
result = Hash.new{0}
self.each {|a| result[a] += 1}
result
end
end

a = ["a", "a", "a", "b", "c", "d", "d"]
c = a.counts
a.reject{|b| c == 1} # => ["a", "a", "a", "d", "d"]
 
D

Drew Olson

Jason said:
I'm basically trying to the opposite of .uniq Let's say I have an array:

["a", "a", "a", "b", "c", "d", "d"]

I would like to boil this down by first tossing the values that only
appear once. Which leaves me with:

["a", "a", "a", "d", "d"]

Then I need to somehow determine that "a" appears 3 times and "d"
appears 2 times. Any help would be great. Thanks

This should work:

a = ["a", "a", "a", "b", "c", "d", "d"]
a.uniq.map{|i| i if (a.map{|j| j if j==i}.length > 1)}
 
R

Ross Bamford

I'm basically trying to the opposite of .uniq Let's say I have an array:

["a", "a", "a", "b", "c", "d", "d"]

I would like to boil this down by first tossing the values that only
appear once. Which leaves me with:

["a", "a", "a", "d", "d"]

Then I need to somehow determine that "a" appears 3 times and "d"
appears 2 times. Any help would be great. Thanks.

There are bound to be better ways, but you could try something like:

a = ["a", "a", "a", "b", "c", "d", "d"]
# => ["a", "a", "a", "b", "c", "d", "d"]

hsh = a.inject(Hash.new{|h,k| h[k] = 0}) {|h,v| h[v] += 1 ; h}.reject!
{|k,v| v == 1}
# => {"a"=>3, "d"=>2}

hsh['a']
# => 3
hsh['d']
# => 2

Depending on how you're using the results, You could also run a second
inject to reverse the lookup:

rhsh = hsh.inject(Hash.new { |h,k| h[k] = [] }) { |h,(k,v)| h[v] << k ;
h }
# => {2=>["d"], 3=>["a"]}

(2..rhsh.keys.max).each { |i| puts "count #{i}: #{rhsh}" }
# count 2: d
# count 3: a

If you went with this way, I guess you could forget about getting rid of
the 1s with reject!, since you could just ignore the 1 key in the
rhsh...
 
D

Drew Olson

Drew said:
Jason said:
I'm basically trying to the opposite of .uniq Let's say I have an array:

["a", "a", "a", "b", "c", "d", "d"]

I would like to boil this down by first tossing the values that only
appear once. Which leaves me with:

["a", "a", "a", "d", "d"]

Then I need to somehow determine that "a" appears 3 times and "d"
appears 2 times. Any help would be great. Thanks

This should work:

a = ["a", "a", "a", "b", "c", "d", "d"]
a.uniq.map{|i| i if (a.map{|j| j if j==i}.length > 1)}

Actually, so that you have them appear more than once you'd need to do:

a = ["a", "a", "a", "b", "c", "d", "d"]
a.map{|i| i if (a.map{|j| j if j==i}.length > 1)}
 
J

Jamey Cribbs

Jason said:
I'm basically trying to the opposite of .uniq Let's say I have an array:

["a", "a", "a", "b", "c", "d", "d"]

I would like to boil this down by first tossing the values that only
appear once. Which leaves me with:

["a", "a", "a", "d", "d"]

Then I need to somehow determine that "a" appears 3 times and "d"
appears 2 times. Any help would be great. Thanks.
Here's what I whipped up:

letters = ['a','a','a','b','c','d','d']
counts = {}

letters.each do |letter|
count = letters.find_all { |x| x == letter }.size
counts[letter] = count if count > 1
end

non_unique_letters = letters.find_all { |x| counts.has_key?(x) }

p counts
p non_unique_letters

Jamey

Confidentiality Notice: This email message, including any attachments, is for the sole use of the intended recipient(s) and may contain confidential and/or privileged information. If you are not the intended recipient(s), you are hereby notified that any dissemination, unauthorized review, use, disclosure or distribution of this email and any materials contained in any attachments is prohibited. If you receive this message in error, or are not the intended recipient(s), please immediately notify the sender by email and destroy all copies of the original message, including attachments.
 
W

WATANABE Hirofumi

Hi,

Jason Burgett said:
I'm basically trying to the opposite of .uniq Let's say I have an array:

["a", "a", "a", "b", "c", "d", "d"]

I would like to boil this down by first tossing the values that only
appear once. Which leaves me with:

["a", "a", "a", "d", "d"]

Then I need to somehow determine that "a" appears 3 times and "d"
appears 2 times. Any help would be great. Thanks.

% irb
a = ["a", "a", "a", "b", "c", "d", "d"] => ["a", "a", "a", "b", "c", "d", "d"]
a.select{|i| a.grep(i).size > 1}
=> ["a", "a", "a", "d", "d"]
 
M

Mike Fletcher

Jason said:
I'm basically trying to the opposite of .uniq Let's say I have an array:

["a", "a", "a", "b", "c", "d", "d"]

I would like to boil this down by first tossing the values that only
appear once. Which leaves me with:

["a", "a", "a", "d", "d"]

Then I need to somehow determine that "a" appears 3 times and "d"
appears 2 times. Any help would be great. Thanks.

a = %w( a a a b c d d )
want_and_counts = a.inject( Hash.new(0) ) { |h,x| h[x]+=1; h }.reject {
|k,v| v == 1 }
 
P

Phrogz

WATANABE said:
Jason Burgett said:
["a", "a", "a", "d", "d"]

Then I need to somehow determine that "a" appears 3 times and "d"
appears 2 times. Any help would be great. Thanks.

% irb
a = ["a", "a", "a", "b", "c", "d", "d"] => ["a", "a", "a", "b", "c", "d", "d"]
a.select{|i| a.grep(i).size > 1}
=> ["a", "a", "a", "d", "d"]

Taking that one step further to meet his second need:

class Array
def duplicates_count
uniq.map{ |e|
if ( count = grep(e).size ) > 1
{ e => count }
end
}.compact
end
end

a = ["a", "a", "a", "b", "c", "d", "d"]
p a.duplicates_count
#=> [{"a"=>3}, {"d"=>2}]
 
A

ara.t.howard

WATANABE said:
Jason Burgett said:
["a", "a", "a", "d", "d"]

Then I need to somehow determine that "a" appears 3 times and "d"
appears 2 times. Any help would be great. Thanks.

% irb
a = ["a", "a", "a", "b", "c", "d", "d"]
=> ["a", "a", "a", "b", "c", "d", "d"]
a.select{|i| a.grep(i).size > 1}
=> ["a", "a", "a", "d", "d"]

Taking that one step further to meet his second need:

class Array
def duplicates_count
uniq.map{ |e|
if ( count = grep(e).size ) > 1
{ e => count }
end
}.compact
end
end

a = ["a", "a", "a", "b", "c", "d", "d"]
p a.duplicates_count
#=> [{"a"=>3}, {"d"=>2}]

that's O(n^2) though... the others are O(n)

-a
 
R

Robert Klemme

I'm basically trying to the opposite of .uniq Let's say I have an array:

["a", "a", "a", "b", "c", "d", "d"]

I would like to boil this down by first tossing the values that only
appear once. Which leaves me with:

["a", "a", "a", "d", "d"]

Then I need to somehow determine that "a" appears 3 times and "d"
appears 2 times. Any help would be great. Thanks.

Did anyone suggest inject + partition yet? If not:

irb(main):001:0> a = ["a", "a", "a", "b", "c", "d", "d"]
=> ["a", "a", "a", "b", "c", "d", "d"]
irb(main):002:0> stat = a.inject(Hash.new(0)) do |h, e|
irb(main):003:1* h[e] += 1
irb(main):004:1> h
irb(main):005:1> end
=> {"a"=>3, "b"=>1, "c"=>1, "d"=>2}
irb(main):006:0> one, multi = stat.partition {|e,c| c == 1}
=> [[["b", 1], ["c", 1]], [["a", 3], ["d", 2]]]
irb(main):007:0> one
=> [["b", 1], ["c", 1]]
irb(main):008:0> multi
=> [["a", 3], ["d", 2]]
irb(main):009:0> one.map! {|e,c| e}
=> ["b", "c"]

Kind regards

robert
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,215
Messages
2,571,113
Members
47,708
Latest member
SharonMaes

Latest Threads

Top