How to remove duplicate elements in a 2D array

L

Li Chen

Hi all,

I have a 2D array as follow:

arr=[

[ 'a',1,2,3],
['b',4,5,6],
['c',3,2,1],
['a',1.3,2.2,3,3],
...
['a', 2,1,3],
...
['a',2.1,1.5,3]

]

As you can see there are several rows containing element 'a',
and the last row containing 'a'' is the lastest one. I wonder if it
is possible to return a new array which contains the latest row having
'a' ,
together with other rows.


Thanks,

Li
 
P

Paul Smith

Hi all,

I have a 2D array as follow:

arr=3D[

=A0 =A0 [ 'a',1,2,3],
=A0 =A0 ['b',4,5,6],
=A0 =A0 ['c',3,2,1],
=A0 =A0 ['a',1.3,2.2,3,3],
=A0 =A0 ...
=A0 =A0 ['a', 2,1,3],
=A0 ...
=A0 ['a',2.1,1.5,3]

]

As you can see there are several rows containing =A0element 'a',
and the last row containing 'a'' is the lastest one. =A0I wonder if it
is possible to return a new array which contains the latest row having
'a' ,
together with other rows.

result =3D []
arr.each { |x| result =3D x if x[0] =3D=3D 'a' }
result

?

--=20
Paul Smith
http://www.nomadicfun.co.uk

(e-mail address removed)
 
G

Greg Donald

Hi all,

I have a 2D array as follow:

arr=3D[

=A0 =A0 [ 'a',1,2,3],
=A0 =A0 ['b',4,5,6],
=A0 =A0 ['c',3,2,1],
=A0 =A0 ['a',1.3,2.2,3,3],
=A0 =A0 ...
=A0 =A0 ['a', 2,1,3],
=A0 ...
=A0 ['a',2.1,1.5,3]

]

As you can see there are several rows containing =A0element 'a',
and the last row containing 'a'' is the lastest one. =A0I wonder if it
is possible to return a new array which contains the latest row having
'a' ,
together with other rows.


Something like this perhaps:

arr.collect{ |a| a.include?( 'a' ) ? a : nil }.compact



--=20
Greg Donald
http://destiney.com/
 
J

John W Higgins

[Note: parts of this message were removed to make it a legal post.]

Morning Li,

Hi all,

I have a 2D array as follow:

arr=[

[ 'a',1,2,3],
['b',4,5,6],
['c',3,2,1],
['a',1.3,2.2,3,3],
...
['a', 2,1,3],
...
['a',2.1,1.5,3]

]

As you can see there are several rows containing element 'a',
and the last row containing 'a'' is the lastest one. I wonder if it
is possible to return a new array which contains the latest row having
'a' ,
together with other rows.
This would do it (not sure if it's optimal or not)

seen = []
arr.reverse.select{ |x|
check = !(seen.include?(x[0]))
 
P

Paul Smith

Hi all,

I have a 2D array as follow:

arr=3D[

=A0 =A0 [ 'a',1,2,3],
=A0 =A0 ['b',4,5,6],
=A0 =A0 ['c',3,2,1],
=A0 =A0 ['a',1.3,2.2,3,3],
=A0 =A0 ...
=A0 =A0 ['a', 2,1,3],
=A0 ...
=A0 ['a',2.1,1.5,3]

]

As you can see there are several rows containing =A0element 'a',
and the last row containing 'a'' is the lastest one. =A0I wonder if it
is possible to return a new array which contains the latest row having
'a' ,
together with other rows.

result =3D []
arr.each { |x| result =3D x if x[0] =3D=3D 'a' }
result

?

Sorry, this only solves the "Last row containing a" part of the
problem. If you're intending to eliminate duplicates, then use that
first element as the key to a hash.

result =3D {}
arr.each{|x| result[x[0]] =3D x[1,x.length-1]}

?
--
Paul Smith
http://www.nomadicfun.co.uk

(e-mail address removed)



--=20
Paul Smith
http://www.nomadicfun.co.uk

(e-mail address removed)
 
J

John W Higgins

[Note: parts of this message were removed to make it a legal post.]

Sorry about the last response - hit the wrong button.....

Hi all,

I have a 2D array as follow:

arr=[

[ 'a',1,2,3],
['b',4,5,6],
['c',3,2,1],
['a',1.3,2.2,3,3],
...
['a', 2,1,3],
...
['a',2.1,1.5,3]

]

As you can see there are several rows containing element 'a',
and the last row containing 'a'' is the lastest one. I wonder if it
is possible to return a new array which contains the latest row having
'a' ,
together with other rows.
Lets try this again shall we....

seen = [] # this stores the first elements as we pass through them
#we reverse the array because you want the last elements to be the ones
returned
#select will add an element to the return value if the block is true
arr.reverse.select{ |x|
check = !(seen.include?(x[0])) #we check to see if we've seen this first
element before
seen << x[0] #add the first element to our "seen" list - we don't care if
it's in here multiple times
check #the block equals the check value
}.reverse #since we reversed once we want to do so again to get the correct
order back

John
 
P

Paul Smith

Hi all,

I have a 2D array as follow:

arr=3D[

=A0 =A0 [ 'a',1,2,3],
=A0 =A0 ['b',4,5,6],
=A0 =A0 ['c',3,2,1],
=A0 =A0 ['a',1.3,2.2,3,3],
=A0 =A0 ...
=A0 =A0 ['a', 2,1,3],
=A0 ...
=A0 ['a',2.1,1.5,3]

]

As you can see there are several rows containing =A0element 'a',
and the last row containing 'a'' is the lastest one. =A0I wonder if it
is possible to return a new array which contains the latest row having
'a' ,
together with other rows.

result =3D []
arr.each { |x| result =3D x if x[0] =3D=3D 'a' }
result

?

Sorry, this only solves the "Last row containing a" part of the
problem. =A0If you're intending to eliminate duplicates, then use that
first element as the key to a hash.

result =3D {}
arr.each{|x| result[x[0]] =3D x[1,x.length-1]}

I knew there was a a better way to do this:

arr.each{|x| result[x[0]] =3D x[1..-1]}

is a more succinct second line.

--=20
Paul Smith
http://www.nomadicfun.co.uk

(e-mail address removed)
 
T

Thairuby ->a, b {a + b}

I think you want an array with unique member. If the member is not
unique
then return the last occur of it.

arr=[[ 'a',1,2,3],
['b',4,5,6],
['c',3,2,1],
['a',1.3,2.2,3,3],
['a', 2,1,3],
['a',2.1,1.5,3]]

result = arr.inject({}){|x,y| x[y[0]]=y[1..-1]; x}.map(&:flatten)
p result

# [["a", 2.1, 1.5, 3], ["b", 4, 5, 6], ["c", 3, 2, 1]]
 
L

Li Chen

Yes this is what I want. But can you explain the code line in a little
bit more?

Thanks,

Li

I think you want an array with unique member. If the member is not
unique
then return the last occur of it.

arr=[[ 'a',1,2,3],
['b',4,5,6],
['c',3,2,1],
['a',1.3,2.2,3,3],
['a', 2,1,3],
['a',2.1,1.5,3]]

result = arr.inject({}){|x,y| x[y[0]]=y[1..-1]; x}.map(&:flatten)
p result

# [["a", 2.1, 1.5, 3], ["b", 4, 5, 6], ["c", 3, 2, 1]]
 
D

David A. Black

Hi --

I think you want an array with unique member. If the member is not
unique
then return the last occur of it.

arr=[[ 'a',1,2,3],
['b',4,5,6],
['c',3,2,1],
['a',1.3,2.2,3,3],
['a', 2,1,3],
['a',2.1,1.5,3]]

result = arr.inject({}){|x,y| x[y[0]]=y[1..-1]; x}.map(&:flatten)
p result

# [["a", 2.1, 1.5, 3], ["b", 4, 5, 6], ["c", 3, 2, 1]]

Another way, at least in 1.9:

result = Hash[arr.map {|a| [a.first, a] }].values


David
 
T

Thairuby ->a, b {a + b}

The main idea is to use Hash and use the first letter ("a","b","c") as a
key. I do it with "inject" and David do it with "Hash[]"

I change your arr

arr=[[ 'a',1,2,3],
['b',4,5,6],
['c',3,2,1],
['a',1.3,2.2,3,3],
['a', 2,1,3],
['a',2.1,1.5,3]]

to a hash

hash={'a'=>[1,2,3],
'b'=>[4,5,6],
'c'=>[3,2,1],
'a'=>[1.3,2.2,3,3],
'a'=>[2,1,3],
'a'=>[2.1,1.5,3]}

You can see that key "a" does repeat 4 times in hash. So, the last one
will replace the first one automatically.

finally,

hash = {"a"=>[2.1, 1.5, 3], "b"=>[4, 5, 6], "c"=>[3, 2, 1]}

The method ".map(&:flatten)" is use to converse hash back to array.
 
D

David A. Black

Hi --

I think you want an array with unique member. If the member is not
unique
then return the last occur of it.

arr=[[ 'a',1,2,3],
['b',4,5,6],
['c',3,2,1],
['a',1.3,2.2,3,3],
['a', 2,1,3],
['a',2.1,1.5,3]]

result = arr.inject({}){|x,y| x[y[0]]=y[1..-1]; x}.map(&:flatten)
p result

# [["a", 2.1, 1.5, 3], ["b", 4, 5, 6], ["c", 3, 2, 1]]

You could economize on intermediate objects and method calls like
this:

result = arr.inject({}){|x,y| x[y[0]]=y; x}.values

That way, you don't take the first element out, so you don't have to
put it back.


David
 
L

Li Chen

Thairuby said:
The method ".map(&:flatten)" is use to converse hash back to array.


Thanks.

But I still have some doubts about this line:

result = arr.inject({}){|x,y| x[y[0]]=y[1..-1];
x
}.map(&:flatten)


1) When I look at #inject method, I notice it only takes either no
augument or
an augument with initial value. But in your code it takes a hash. It is
kind of new/strange to me.

2) Why do you write an x alone after this line x[y[0]]=y[1..-1]; ?

3) #map(&:flatten) doesn't work in my Ruby version(1.8.6). Which version
are you using? But I make some changes and it works.

result = arr.inject( { } ){|x,y| x[y[0]]=y[1..-1]; x}.to_a
r=result.collect { |row| row.flatten}

Li
 
R

Rick DeNatale

Thairuby said:
The method ".map(&:flatten)" is use to converse hash back to array.


Thanks.

But I still have some doubts about this line:

=A0result =3D arr.inject({}){|x,y| x[y[0]]=3Dy[1..-1];
=A0 =A0 =A0 =A0 =A0 =A0 =A0 x
=A0 =A0 =A0 =A0 =A0 =A0 =A0}.map(&:flatten)


1) When I look at #inject method, I notice it only takes either no
augument or
an augument with initial value. But in your code it takes a hash. It is
kind of new/strange to me.

It's giving it an empty hash as the initial value, nothing strange at all.

2) Why do you =A0write an x alone after this line =A0x[y[0]]=3Dy[1..-1]; =
?

So that the result of the block is the hash and not y[1..-1] which is
the value of the assignment.
3) #map(&:flatten) doesn't work in my Ruby version(1.8.6). Which version
are you using? But I make some changes and it works.

result =3D arr.inject( { =A0} =A0){|x,y| x[y[0]]=3Dy[1..-1]; x}.to_a
r=3Dresult.collect { |row| row.flatten}

I'm guessing that he's using either 1.8.7 or 1.9 both of which define
Symbol#to_proc which allows this, or perhaps he's using the
activesupport gem which is part of Rails and defines this as well.


I like David Black's alternative suggestion:

result =3D arr.inject({}){|x,y| x[y[0]]=3Dy; x}.values

Note that neither of these preserve the ordering of the rows retained
from the original array in Ruby 1.8.x, but if you're happy I guess
that's not a requirement.

--=20
Rick DeNatale

Blog: http://talklikeaduck.denhaven2.com/
Twitter: http://twitter.com/RickDeNatale
WWR: http://www.workingwithrails.com/person/9021-rick-denatale
LinkedIn: http://www.linkedin.com/in/rickdenatale
 
H

Harry Kakueki

Hi all,

I have a 2D array as follow:

arr=[

[ 'a',1,2,3],
['b',4,5,6],
['c',3,2,1],
['a',1.3,2.2,3,3],
...
['a', 2,1,3],
...
['a',2.1,1.5,3]

]

As you can see there are several rows containing element 'a',
and the last row containing 'a'' is the lastest one. I wonder if it
is possible to return a new array which contains the latest row having
'a' ,
together with other rows.


Thanks,

Li

Here is another way.
It works with Ruby 1.8.6.


p arr.map{|x| x[0]}.uniq.map{|y| arr.reverse.detect{|z| z[0] == y}}

# OR if order is important,
#

p arr.reverse.map{|x| x[0]}.uniq.reverse.map{|y|
arr.reverse.detect{|z| z[0] == y}}


Harry
 
D

David A. Black

Hi --

I like David Black's alternative suggestion:

result = arr.inject({}){|x,y| x[y[0]]=y; x}.values

Note that neither of these preserve the ordering of the rows retained
from the original array in Ruby 1.8.x, but if you're happy I guess
that's not a requirement.

And in 1.9, you can do this:

arr.map.with_object({}) {|e,h| h[e[0]] = e }.values

which gets rid of the need to force the accumulator to be the hash
every time through.


David
 
P

pharrington

Thairuby said:
The method ".map(&:flatten)" is use to converse hash back to array.

Thanks.

But I still have some doubts about this line:

 result = arr.inject({}){|x,y| x[y[0]]=y[1..-1];ash.
               x
              }.map(&:flatten)

1) When I look at #inject method, I notice it only takes either no
augument or
an augument with initial value. But in your code it takes a hash. It is
kind of new/strange to me.

The argument passed to inject is the initial value. Thus, we just want
the routine to start off with an empty hash.
2) Why do you  write an x alone after this line  x[y[0]]=y[1..-1]; ?

The value of the first argument to the block passed to inject is set
to the return value of the block. In Ruby everything's an expression,
so the return value of a method or block is just that of the last
expression run.
3) #map(&:flatten) doesn't work in my Ruby version(1.8.6). Which version
are you using? But I make some changes and it works.

I don't have Ruby 1.8.6 installed and its been forever since I
remember using it last, so (works in 1.8.7 and up though) :\



Also, here's a rather nasty version that retains the order of the
initial array's elements, if that's important:

irb(main):050:0> arry = [['a', 1, 2, 3], ['b', 4, 5, 6], ['c', 3, 2,
1], ['a', 1.3, 2.2, 3, 3], ['a', 2, 1, 3], ['a', 2.1, 1.5, 3]]
=> [["a", 1, 2, 3], ["b", 4, 5, 6], ["c", 3, 2, 1], ["a", 1.3, 2.2, 3,
3], ["a", 2, 1, 3], ["a", 2.1, 1.5, 3]]
irb(main):051:0> arry.reverse.inject([]) {|r, e| r.unshift(e) if r.map
{|x| x.first if x.first == e.first}.compact.size == 0; r}
=> [["b", 4, 5, 6], ["c", 3, 2, 1], ["a", 2.1, 1.5, 3]]
 
P

pharrington

r.map {|x| x.first if x.first == e.first}.compact.size == 0

I'm an idiot, Ruby likes to call this pile of silliness "detect":

arry.reverse.inject([]) {|r, e| r.unshift(e) unless r.detect {|x|
x.first == e.first}; r}
 
D

David A. Black

Hi --

r.map {|x| x.first if x.first == e.first}.compact.size == 0

I'm an idiot, Ruby likes to call this pile of silliness "detect":

arry.reverse.inject([]) {|r, e| r.unshift(e) unless r.detect {|x|
x.first == e.first}; r}

Couldn't resist throwing in another 1.9-esque version:

arr.group_by(&:first).map(&:last).map(&:last)

:) Of course mapping to [-1][-1] would work too (and probably more
efficiently).


David
 

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

Forum statistics

Threads
473,744
Messages
2,569,479
Members
44,899
Latest member
RodneyMcAu

Latest Threads

Top