Array question

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.

Thank you for your input.

Li

#########
C:>irb
irb(main):001:0> array=[1,2,3,4,5,6,7,8,9,10]
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
irb(main):002:0> array_new=Array.new
=> []
irb(main):003:0> array.each do |e|
irb(main):004:1* array_new<<"#{e}"
irb(main):005:1> array_new<<"#{e}"
irb(main):006:1> end
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
irb(main):007:0>
irb(main):008:0* array_new.delete_at(0)
=> “1”
irb(main):009:0> array_new.delete_at(array_new.size-1)
=> “10”
irb(main):010:0> puts array_new
1
2
2
3
3
4
4
5
5
6
6
7
7
8
8
9
9
10
=> nil

On 11/20/06, Li Chen [email protected] 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.

A couple of ways: (there are probably dozens more)

given:

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

1.

new_array = array[1…-2]

2.

new_array = array.dup
new_array.shift # shifts off the first element
new_array.pop # pops off the last element

#1 uses Array#[], which is a synonym for Array#slice.
1…-2 is a Range parameter. In this case, it says you want a slice of
the array containing everything but the first and last element.

On 11/21/06, Wilson B. [email protected] wrote:

A couple of ways: (there are probably dozens more)

given:

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

1.

new_array = array[1…-2]

This would not duplicate the inner elements though.

To build on this one.
b = a.inject( [] ) { |a,e| a << [e,e]}.flatten[1…-2]

This looks pretty jarring to my eyes though :frowning:

Hi –

On Tue, 21 Nov 2006, 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.

Here’s one way, along with some tests:

require ‘test/unit’

class ChangeArrayTest < Test::Unit::TestCase

def change_array(a)
[*1…a.size-1].reverse.each {|i| a.insert(i,a[i]) }
a
end

def test_empty_array
assert_equal([], change_array([]))
end

def test_one_element_array
assert_equal([1], change_array([1]))
end

def test_two_element_array
assert_equal([1,2], change_array([1,2]))
end

def test_three_element_array
assert_equal([1,2,2,3], change_array([1,2,3]))
end

def test_ten_element_array
assert_equal([1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10],
change_array([1,2,3,4,5,6,7,8,9,10]))
end
end

David

On 11/21/06, Daniel N [email protected] wrote:

array = [1,2,3,4,5,6]
This looks pretty jarring to my eyes though :frowning:

Sorry missed a brace on the block. Should be

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

Hi –

On Tue, 21 Nov 2006, Wilson B. wrote:

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

1.

new_array = array[1…-2]

2.

new_array = array.dup
new_array.shift # shifts off the first element
new_array.pop # pops off the last element

I think Li wanted to duplicate elements in the sense of:

[1,2,3,4,5] => [1,2,2,3,3,4,4,5]

David

Hi –

On Tue, 21 Nov 2006, Daniel N wrote:

On 11/21/06, Daniel N [email protected] wrote:

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.

David

On 11/21/06, Li Chen [email protected] wrote:

new_array.shift # shifts off the first element
new_array = array.dup
new_array.shift
new_array.pop
I get [2,3,4] but I want this result [1,2,2,3,3,4,4,5]

Li

Yeah. Sorry. I didn’t read your question clearly enough. I apologize
for confusing the issue. Check out the other replies from people with
better reading comprehension late at night.
Heh.

Daniel N wrote:

new_array = array[1…-2]

This would not duplicate the inner elements though.

To build on this one.
b = a.inject( [] ) { |a,e| a << [e,e]}.flatten[1…-2]

This looks pretty jarring to my eyes though :frowning:

I would use map over inject into []:

b = a.map { |each| [each,each] }.flatten[1…-2]

Either that, or recognize that it’s equivalent duplicating all except
first and last.

b = [a[0], a[1…-2].map { |each| [each, each] }.flatten, a[-1]]

In either case, we can avoid the flatten entirely, at the expense of
returning to inject:

b = a.inject([]) {|sum, each| sum.concat([each, each])[1…-2]
or
b = [a[0],
a[1…-2].inject([]) {|sum, each| sum.concat([each, each])},
a[-1]]

Of course, if we know the items in the array are in ascending order, we
can /really/ cheat:

b = (a*2).sort[1…-2]

That’s as many as I could think of.

J. B. Rainsberger wrote:

Of course, if we know the items in the array are in ascending order, we
can /really/ cheat:

b = (a*2).sort[1…-2]

That’s as many as I could think of.

Thank you but elements in the array are in random order.

Li

A couple of ways: (there are probably dozens more)

given:

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

1.

new_array = array[1…-2]

2.

new_array = array.dup
new_array.shift # shifts off the first element
new_array.pop # pops off the last element

#1 uses Array#[], which is a synonym for Array#slice.
1…-2 is a Range parameter. In this case, it says you want a slice of
the array containing everything but the first and last element.

Hi,

I run array=[1,2,3,4,5]
new_array = array.dup
new_array.shift
new_array.pop
I get [2,3,4] but I want this result [1,2,2,3,3,4,4,5]

Li

On_Behalf_Of_Li_Chen:

I run array=[1,2,3,4,5]

new_array = array.dup

new_array.shift

new_array.pop

I get [2,3,4] but I want this result [1,2,2,3,3,4,4,5]

but you can build from there. you just have to assemble it back,
something like,

irb(main):031:0> array=[1,2,3,4,5]
=> [1, 2, 3, 4, 5]
irb(main):032:0> new_array = array.dup
=> [1, 2, 3, 4, 5]
irb(main):035:0> first = new_array.shift
=> 1
irb(main):036:0> last = new_array.pop
=> 5
irb(main):037:0> new_array
=> [2, 3, 4]
irb(main):038:0> new_array.map{|e| [e,e]}.flatten
=> [2, 2, 3, 3, 4, 4]
irb(main):039:0> new_array.map{|e| [e,e]}.flatten.unshift(first)
=> [1, 2, 2, 3, 3, 4, 4]
irb(main):040:0> new_array.map{|e| [e,e]}. flatten. unshift(first).
push(last)
=> [1, 2, 2, 3, 3, 4, 4, 5]
irb(main):041:0>

kind regards -botp

Li

Thank you all for your invalulabe inputs.

Based on what I understand and my personal preference I choose the
following scripts.

Li

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
Exit code: 0

Li Chen wrote:

Thank you all for your invalulabe inputs.

Based on what I understand and my personal preference I choose the
following scripts.

Li

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
Exit code: 0

This is the best I could come up with. It works with any kind of element
including embedded arrays.

array_new = (temp = array[1…-2]).zip(temp).inject([]){|n, e|
n.concat(e) }

irb(main):148:0> array=[1,[2,3],4,5,{6,7},“8”]
=> [1, [2, 3], 4, 5, {6=>7}, “8”]
irb(main):149:0> (temp = array[1…-2])
=> [[2, 3], 4, 5, {6=>7}]
irb(main):150:0> (temp = array[1…-2]).zip(temp)
=> [[[2, 3], [2, 3]], [4, 4], [5, 5], [{6=>7}, {6=>7}]]
irb(main):151:0> (temp = array[1…-2]).zip(temp).inject([]) {|new, elem|
new.concat(elem) }
=> [[2, 3], [2, 3], 4, 4, 5, 5, {6=>7}, {6=>7}]

Li Chen schrieb:

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.

c = a.size > 2 ? a[0, 1] + a[1…-2].inject([]) { |b,x| b << x << (x.dup
rescue x) } + a[-1, 1] : a

Edwin F. 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.

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(' ')]

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]

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

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

[email protected] wrote:

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.

I’m surprised nobody has suggested:

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

Cheers,

Pete Y.