Array question

dblack@wobblini.net wrote:

Hi --

Sorry missed a brace on the block. Should be

b = a.inject( ) { |a,e| a << [e,e]}.flatten[1..-2] }

I so much think it's time for flatten to take an argument, specifying
number of levels to flatten. My flattenx extension does this, and it
seems like a natural fit for flatten itself. Otherwise almost all
flatten-based techniques run the risk of over-flattening.

100% agreement here. The problem is easily solved if you ignore the
flattening risk:

a = (1..5).to_a
p a[0..-2].zip(a[1..-1]).flatten
   # ==> [1, 2, 2, 3, 3, 4, 4, 5]

The inject solution is a little less elegant and probably much less
efficient:

p a[0..-2].zip(a[1..-1]).inject{|s,x| s+x}
   # ==> [1, 2, 2, 3, 3, 4, 4, 5]

Maybe a little more efficient:

p a[0..-2].zip(a[1..-1]).inject{|s,x| s.concat x}

I've gotten out the the habit of using #zip for exactly this reason: you
have to flatten the output to get what you really want, but then you
might get something you really don't want.

···

On Tue, 21 Nov 2006, Daniel N wrote:

On 11/21/06, Daniel N <has.sox@gmail.com> wrote:

--
       vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

Robert Klemme wrote:

> Robert Klemme wrote:
>>> Li Chen wrote:
>>>> Hi all,
>>>>
>>>> I want to build a new array from an old one with every element being
>>>> duplicated except the first and last element. And here are my codes. I
>>>> wonder if this is a real Ruby way to do it.
>>>>> array=[1,2,3,4,5,6,7,8,9,10]
>>> => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>>>> array.zip(array).flatten[1..-2]
>>> => [1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10]
>> That's cute! I have another one with #inject (of course):
>>
>> irb(main):007:0> arr=(1..10).to_a
>> => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>> irb(main):008:0> copy=
>> =>
>> irb(main):009:0> arr.inject{|a,b| copy << a << b; b}
>> => 10
>> irb(main):010:0> copy
>> => [1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10]
>
> This demonstrates an excellent understanding of inject and

Thank you!

> is a good way to eliminate the somewhat ugly [1..-2].

Well, you can use [1...-1] instead. :slight_smile:

Ha, ha. Also ugly.

> Here's a prolix way of avoiding array indexing while using zip:
>
> copy=arr.dup; copy.shift; copy.pop
> arr.zip(copy).flatten.compact

You can copy and reduce in one step:

>> arr=(1..10).to_a
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>> arr.zip(arr[1...-1]).flatten.compact
=> [1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10]

Or, more efficient

>> arr.zip(arr[1...-1]).flatten!.compact!
=> [1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10]

But it has "[1...-1]"! Indexing with literal numbers is so horrid.

···

On 21.11.2006 10:16, William James wrote:
>> On 21.11.2006 08:18, William James wrote:

Cheers

  robert

Edwin Fine wrote:

Li Chen wrote:

####
array=[1,2,3,4,5]

array_new=Array.new
array.each {|e| array_new<<[e,e]}

array_new.flatten![1..-2].each{|e| print "#{e}\t" }

puts

##output

ruby sur3.rb

1 2 2 3 3 4 4 5

I don't know why we need flatten, it is flatten before you do it.

[code]
array_new=
array.each {|e| array_new+=[e,e]} #use << will get the same result
array_new = array_new[1..-2]
puts [array_new.join(' ')]
[/code]

···

--
Posted via http://www.ruby-forum.com/\.

I love Enumerator so much =)

     require 'enumerator'

     class Array
       def duplicate_internal
         to_enum(:each_cons,2).inject([]) do |r, c|
           r + c
         end
       end
     end

     p [1,2,3,4].duplicate_internal # => [1, 2, 2, 3, 3, 4]

···

--
Lou.

And a more efficient solution (thanks to Reid Morrison) is

array_new = (1..array.length - 2).inject([]) {|arr, i| arr << array[i]
<< array[i]}

···

--
Posted via http://www.ruby-forum.com/.

I'm surprised nobody has suggested:

   a.zip(a).flatten[1..-2]

Cheers,

Pete Yandell

I benchmarked the various suggestions and added some of my own, and the
results may surprise some (they surprised me!)

Rehearsal --------------------------------------------------------------
:zip_flatten_technique 8.280000 0.000000 8.280000 ( 19.532383)
:enum_cons_technique 18.190000 0.020000 18.210000 ( 28.552418)
:copy_inject_technique 5.500000 0.000000 5.500000 ( 5.567597)
:fill_technique 5.250000 0.010000 5.260000 ( 5.373526)
:naive_technique 5.040000 0.000000 5.040000 ( 5.091017)
:flatten_compact_technique 7.900000 0.000000 7.900000 ( 7.938109)
:original_technique 2.560000 0.000000 2.560000 ( 2.620933)
:zip_concat_technique 6.480000 0.000000 6.480000 ( 6.493874)
---------------------------------------------------- total: 59.230000sec

                                 user system total real
:zip_flatten_technique 7.500000 0.000000 7.500000 ( 7.636382)
:enum_cons_technique 17.790000 0.680000 18.470000 ( 18.536960)
:copy_inject_technique 5.250000 0.000000 5.250000 ( 5.277144)
:fill_technique 5.070000 0.010000 5.080000 ( 5.165221)
:naive_technique 5.100000 0.010000 5.110000 ( 5.113858)
:flatten_compact_technique 7.330000 0.000000 7.330000 ( 7.350595)
:original_technique 2.590000 0.000000 2.590000 ( 2.600783)
:zip_concat_technique 6.190000 0.000000 6.190000 ( 6.197641)

So the OP's technique, if not the prettiest, is actually by far the
fastest, and is correct when used with nested arrays. Some of the other
solutions (that use flatten) are incorrect when used with nested arrays.

Here's the code:

require 'benchmark'
require 'enumerator'

techniques = {
  :enum_cons_technique => lambda {|arr|
arr.enum_cons(2).inject([]) {|a, i| a+i}},
  :zip_flatten_technique => lambda {|arr|
arr.zip(arr).flatten[1..-2]},
  :copy_inject_technique => lambda {|arr| copy = [];
arr.inject{|a,b| copy << a << b; b}; copy},
  :flatten_compact_technique => lambda {|arr|
arr.zip(arr[1...-1]).flatten!.compact!},
  :zip_concat_technique => lambda {|arr| arr.zip(arr).inject([])
{|n, e| n.concat(e)}[1..-2]},
  :fill_technique => lambda {|arr| (Array.new((arr.length <<
1) - 2) {|i| arr[i >> 1]})[1..-2]},
  :naive_technique => lambda do |arr|
                                   a = Array.new((arr.length * 2) - 2)
                                   a[0] = arr.first
                                   a[a.length - 1] = arr.last
                                   n = 1
                                   1.upto(arr.length - 2) {|i| a[n] =
a[n+1] = arr[i]; n += 2}
                                   a
                                end,
  :original_technique => lambda do |arr|
                                  array_new = Array.new
                                  arr.each {|e| array_new << e << e}
                                  array_new.delete_at(0)
                                  array_new.delete_at(array_new.size -
1)
                                  array_new
                                end
}

n = 5000
array = ["a", 2, (0..1), /^$/, 1.2345, ["arr"] ] * 100

Benchmark.bmbm do |x|
  techniques.each do |name, proc|
    x.report(name.inspect) { n.times { proc.call(array) } }
  end
end

···

--
Posted via http://www.ruby-forum.com/.

Robert Klemme wrote:
> > Robert Klemme wrote:
> >>> Li Chen wrote:
> >>>> Hi all,
> >>>>
> >>>> I want to build a new array from an old one with every element
being
> >>>> duplicated except the first and last element. And here are my
codes. I
> >>>> wonder if this is a real Ruby way to do it.
> >>>>> array=[1,2,3,4,5,6,7,8,9,10]
> >>> => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
> >>>>> array.zip(array).flatten[1..-2]
> >>> => [1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10]
> >> That's cute! I have another one with #inject (of course):
> >>
> >> irb(main):007:0> arr=(1..10).to_a
> >> => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
> >> irb(main):008:0> copy=
> >> =>
> >> irb(main):009:0> arr.inject{|a,b| copy << a << b; b}
> >> => 10
> >> irb(main):010:0> copy
> >> => [1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10]
> >
> > This demonstrates an excellent understanding of inject and
>
> Thank you!
>
> > is a good way to eliminate the somewhat ugly [1..-2].
>
> Well, you can use [1...-1] instead. :slight_smile:

Ha, ha. Also ugly.

>
> > Here's a prolix way of avoiding array indexing while using zip:
> >
> > copy=arr.dup; copy.shift; copy.pop
> > arr.zip(copy).flatten.compact
>
> You can copy and reduce in one step:
>
> >> arr=(1..10).to_a
> => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
> >> arr.zip(arr[1...-1]).flatten.compact
> => [1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10]
>
> Or, more efficient
>
> >> arr.zip(arr[1...-1]).flatten!.compact!
> => [1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10]

But it has "[1...-1]"! Indexing with literal numbers is so horrid.

>
> Cheers
>
> robert

your name looks like a chinese , and i'm too.

···

2006/11/23, William James <w_a_x_man@yahoo.com>:

> On 21.11.2006 10:16, William James wrote:
> >> On 21.11.2006 08:18, William James wrote:

what about this code ?

irb(main):008:0> %w{a b c d e f g h i j k l m n o p q r s t u v w x y
z}.map{|i| i = [i,i]}.flatten[1..-2]
=> ["a", "b", "b", "c", "c", "d", "d", "e", "e", "f", "f", "g", "g",
"h", "h", "i", "i", "j", "j", "k", "k", "l", "l", "m", "m", "n", "n",
"o", "o", "p", "p", "q", "q", "r", "r", "s", "s", "t", "t", "u", "u",
"v", "v", "w", "w", "x", "x", "y", "y", "z"]

Need to take care of size == 1

     class Array
      def duplicate_internal
        return self if size == 1 # added
        to_enum(:each_cons,2).inject([]) do |r, c|
          r + c
        end
      end
    end

···

--
Lou.

Hi --

···

On Wed, 22 Nov 2006, Pete Yandell wrote:

I'm surprised nobody has suggested:

a.zip(a).flatten[1..-2]

I decided to avoid flatten (see my earlier comments). To be
flatten-safe, you could do:

   require 'flattenx' # [1]
   a.zip(a).flatten_once[1..-2]

David

[1] http://www.rubypal.com/ruby/flattenx

--
                   David A. Black | dblack@rubypal.com
Author of "Ruby for Rails" [1] | Ruby/Rails training & consultancy [3]
DABlog (DAB's Weblog) [2] | Co-director, Ruby Central, Inc. [4]
[1] Ruby for Rails | [3] http://www.rubypowerandlight.com
[2] http://dablog.rubypal.com | [4] http://www.rubycentral.org

Pete Yandell [mailto:pete.yandell@gmail.com] :
# I'm surprised nobody has suggested:
# a.zip(a).flatten[1..-2]

indeed. that's a nice surprise. thanks for the tip.
kind regards -botp

# Cheers,
# Pete Yandell

···

#
#

I'm surprised nobody has suggested

  require 'enumerator'

  ary.enum_cons(2).entries.flatten

Cheers,
Daniel

···

On Wed, 2006-11-22 at 07:36 +0900, Pete Yandell wrote:

I'm surprised nobody has suggested:

   a.zip(a).flatten[1..-2]

So the OP's technique, if not the prettiest, is actually by far the
fastest, and is correct when used with nested arrays. Some of the other
solutions (that use flatten) are incorrect when used with nested arrays.

What is meaning for OP here?

Thanks,

Li

···

--
Posted via http://www.ruby-forum.com/\.

ary.enum_cons(2).inject() {|a, i| a+i}

martin

···

On 11/22/06, Daniel Schierbeck <daniel.schierbeck@gmail.com> wrote:

On Wed, 2006-11-22 at 07:36 +0900, Pete Yandell wrote:
> I'm surprised nobody has suggested:
>
> a.zip(a).flatten[1..-2]

I'm surprised nobody has suggested

  require 'enumerator'

  ary.enum_cons(2).entries.flatten

Original Poster

···

On 22-Nov-06, at 6:19 PM, Li Chen wrote:

So the OP's technique, if not the prettiest, is actually by far the
fastest, and is correct when used with nested arrays. Some of the other
solutions (that use flatten) are incorrect when used with nested arrays.

What is meaning for OP here?

--

Mike Stok <mike@stok.ca>
http://www.stok.ca/~mike/

The "`Stok' disclaimers" apply.

Li Chen wrote:

So the OP's technique, if not the prettiest, is actually by far the
fastest, and is correct when used with nested arrays. Some of the other
solutions (that use flatten) are incorrect when used with nested arrays.

What is meaning for OP here?

Thanks,

Li

Sorry, OP = original poster (that would be YOU :slight_smile:

···

--
Posted via http://www.ruby-forum.com/\.