Concatenating variable number of Arrays ?

J

Johan Holmberg

Hi!

I have been searching for a method to concatenate several arrays.
A had an Array like this

arr1 = [ [1,2,3], [4,5,6], [7,[8,9]] ]

and wanted to produce

arr2 = [1, 2, 3, 4, 5, 6, 7, [8, 9]]

Looking at the methods of the Array class I found some methods that
do similar things, but none that fits exactly:

+ concatenate TWO Arrays

concat concatenate TWO arrays destructively modifying
the first

flatten RECURSIVELY flattens elements into a new Array


The best way I have found until now, of doing what I want is

arr2 = arr1.inject([]) {|acc,x| acc.concat x}


But I feel that the thing I want to do (concatenating a variable
number arrays) is such a natural thing, that it deserves a cleaner
way of being expressed.

Have I missed some other method that does this in a readable way ?

/Johan Holmberg
 
R

Robert Klemme

Johan Holmberg said:
Hi!

I have been searching for a method to concatenate several arrays.
A had an Array like this

arr1 = [ [1,2,3], [4,5,6], [7,[8,9]] ]

and wanted to produce

arr2 = [1, 2, 3, 4, 5, 6, 7, [8, 9]]

Looking at the methods of the Array class I found some methods that
do similar things, but none that fits exactly:

+ concatenate TWO Arrays

concat concatenate TWO arrays destructively modifying
the first

flatten RECURSIVELY flattens elements into a new Array


The best way I have found until now, of doing what I want is

arr2 = arr1.inject([]) {|acc,x| acc.concat x}

That's exactly what I'd do.
But I feel that the thing I want to do (concatenating a variable
number arrays) is such a natural thing, that it deserves a cleaner
way of being expressed.

All other ways that I can think of are more verbose and not necessarily
cleander - let alone faster. The version using each would look like

arr2=[]
arr1.each {|x| arr2.concat x}

Not really an improvement IMHO.
Have I missed some other method that does this in a readable way ?

I don't think so.

robert
 
A

Andre Nathan

A had an Array like this

arr1 = [ [1,2,3], [4,5,6], [7,[8,9]] ]

and wanted to produce

arr2 = [1, 2, 3, 4, 5, 6, 7, [8, 9]]

Someone will probably come up with something better, but you could do it
with

arr1 = [ [1,2,3], [4,5,6], [7,[8,9]] ]
arr2 = []
arr1.each { |array| arr2 += array }

HTH.

Andre
 
S

Simon Strandgaard

I have been searching for a method to concatenate several arrays.
A had an Array like this

arr1 = [ [1,2,3], [4,5,6], [7,[8,9]] ]

and wanted to produce

arr2 = [1, 2, 3, 4, 5, 6, 7, [8, 9]] [snip]
But I feel that the thing I want to do (concatenating a variable
number arrays) is such a natural thing, that it deserves a cleaner
way of being expressed.


how about arr1.flatten_once ?

http://raa.ruby-lang.org/list.rhtml?name=flattenx
 
F

Florian Frank

The best way I have found until now, of doing what I want is

arr2 = arr1.inject([]) {|acc,x| acc.concat x}

You can also use "+" here:

arr2 = arr1.inject([]) { |a,x| a + x }
But I feel that the thing I want to do (concatenating a variable
number arrays) is such a natural thing, that it deserves a cleaner
way of being expressed.

If you extend Symbol like this:

class Symbol
def to_proc
lambda { |a, b| a.__send__(self, b) }
end
end

You could also write

arr2 = arr1.inject([], &:+)

That's pretty similar to how it is done in most functional programming
languages.
 
R

Robert Klemme

Florian Frank said:
The best way I have found until now, of doing what I want is

arr2 = arr1.inject([]) {|acc,x| acc.concat x}

You can also use "+" here:

arr2 = arr1.inject([]) { |a,x| a + x }
But I feel that the thing I want to do (concatenating a variable
number arrays) is such a natural thing, that it deserves a cleaner
way of being expressed.

If you extend Symbol like this:

class Symbol
def to_proc
lambda { |a, b| a.__send__(self, b) }
end
end

You could also write

arr2 = arr1.inject([], &:+)

That's pretty similar to how it is done in most functional programming
languages.

Amazing! Only that it's less efficient since your approach creates more
temporary arrays.

Regards

robert
 
J

Johan Holmberg

If you extend Symbol like this:

class Symbol
def to_proc
lambda { |a, b| a.__send__(self, b) }
end
end

You could also write

arr2 = arr1.inject([], &:+)

That's pretty similar to how it is done in most functional programming
languages.

Amazing! Only that it's less efficient since your approach creates more
temporary arrays.

I agree.
Where can I read about 'to_proc' and how it works ?

To avoid "+" I tried to changing to "concat" and this seems to work too:

class Symbol
def to_proc
lambda { |acc, x| acc.__send__(self, x) }
end
end

arr1 = [ [1,2,3], [4,5,6], [7,[8,9]] ]
arr2 = arr1.inject([], &:concat)

/Johan Holmberg
 
F

Florian Frank

That's true: But it seems very clean to me. ;)
I agree.
Where can I read about 'to_proc' and how it works ?

This trick uses the function block_pass in eval.c. If you pass an object
with & operator to a method, a conversion to a Proc object is attempted
in this function, by calling to_proc on this object. Usually there are
only Method and Proc objects in Ruby that implement this method. But you
can also define your own classes with to_proc if you like. Actually it's
an application of duck typing.
 
J

Johan Holmberg

On 2004-01-16 00:03:39 +0900, Johan Holmberg wrote: [...]
I agree.
Where can I read about 'to_proc' and how it works ?

This trick uses the function block_pass in eval.c. If you pass an object
with & operator to a method, a conversion to a Proc object is attempted
in this function, by calling to_proc on this object. Usually there are
only Method and Proc objects in Ruby that implement this method. But you
can also define your own classes with to_proc if you like. Actually it's
an application of duck typing.

Thanks for the explanation.
Is this documented somewhere or have you found out by reading the
code ?

One thing I wonder is if the 'Symbol.to_proc' method you described
is too global. What if we would like to define the method in a
different way somewhere else ?

Or is it poosible to define 'Symbol.to_proc' in a natural way ?
And in that case, why isn't it already built into Ruby ?

/Johan Holmberg
 
T

Tim Sutherland

Florian Frank said:
The best way I have found until now, of doing what I want is

arr2 = arr1.inject([]) {|acc,x| acc.concat x}

You can also use "+" here:

arr2 = arr1.inject([]) { |a,x| a + x }
[...]

If you're going to do this then you might want to define `sum':
class Array
def sum(start)
inject(start) { |a, x| a + x }
end
end

Then you can do
arr2 = arr1.sum([])

Of course, as Robert Klemme says, this creates many temporary arrays.
 

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,811
Messages
2,569,693
Members
45,478
Latest member
dontilydondon

Latest Threads

Top