Forum: Ruby move to front of array

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
5224d56434a3d237408c217605df8a56?d=identicon&s=25 Payton Swick (Guest)
on 2005-12-21 21:09
(Received via mailing list)
Hi,

I have an array of Strings, and I'd like to find one of the items by
Regexp, then move that item to the front of the array, eg:

array = %w(a B c d Cool e f G)
array.unshift(array.slice!(array.index(array.find { |i| i =~ /cool/i
}))) if array.find { |i| i =~ /cool/i }

Better/cleaner/shorter ways to do it?

-Payton
D84df7c68f790e492c4ad4ec5fe65547?d=identicon&s=25 Florian Frank (Guest)
on 2005-12-21 21:31
(Received via mailing list)
Payton Swick wrote:

> I have an array of Strings, and I'd like to find one of the items by
> Regexp, then move that item to the front of the array, eg:
>
> array = %w(a B c d Cool e f G)
> array.unshift(array.slice!(array.index(array.find { |i| i =~ /cool/i
> }))) if array.find { |i| i =~ /cool/i }


In 1.8.x:

require 'enumerator'
if f = array.enum_for(:each_index).find { |i| array[i] =~ /cool/i }
  array.unshift array.delete_at(f)
end


In 1.9.x you can do:

array.unshift array.delete_at(array.index { |x| x =~ /cool/i })
Cff9eed5d8099e4c2d34eae663aae87e?d=identicon&s=25 Jacob Fugal (Guest)
on 2005-12-21 21:34
(Received via mailing list)
On 12/21/05, Payton Swick <payton@foolord.com> wrote:
>
> -Payton

array = %w(a B c d Cool e f G)
if cool = array.find { |i| i =~ /cool/i }
  array.delete(cool)
  array.unshift(cool)
end

?

Jacob Fugal
E34b5cae57e0dd170114dba444e37852?d=identicon&s=25 Logan Capaldo (Guest)
on 2005-12-21 21:34
(Received via mailing list)
On Dec 21, 2005, at 3:08 PM, Payton Swick wrote:

>
> -Payton
>

I don't know about shorter but...

require 'enumerator'
array = %w(a B c d Cool e f G)
index = array.to_enum(:each_index).find { |i| array[i] =~ /cool/i }
array.unshift( array.delete_at( index ) )
Fe9b2d0628c0943af374b2fe5b320a82?d=identicon&s=25 Eero Saynatkari (rue)
on 2005-12-21 21:38
Payton Swick wrote:
> Hi,
>
> I have an array of Strings, and I'd like to find one of the items by
> Regexp, then move that item to the front of the array, eg:
>
> array = %w(a B c d Cool e f G)
> array.unshift(array.slice!(array.index(array.find { |i| i =~ /cool/i
> }))) if array.find { |i| i =~ /cool/i }
>
> Better/cleaner/shorter ways to do it?

This should work (but without error checking):

array.unshift(array.delete(array.grep(/cool/i).first))

> -Payton


E
Ad97b577f331ae29ed90da5751f2e44f?d=identicon&s=25 Dan Diebolt (dandiebolt)
on 2005-12-21 21:55
(Received via mailing list)
Try this:

  array1 = %w(a B c d Cool e f G)
array1.partition {|i| i =~ /cool/i}
array2=array1.partition {|i| i =~ /cool/i}
array2.flatten     #or array2.flatten!
82e62c756d89bc6fa0a0a2d7f2b1e617?d=identicon&s=25 Ross Bamford (Guest)
on 2005-12-21 22:08
(Received via mailing list)
On Wed, 21 Dec 2005 20:08:34 -0000, Payton Swick <payton@foolord.com>
wrote:

> Hi,
>
> I have an array of Strings, and I'd like to find one of the items by
> Regexp, then move that item to the front of the array, eg:
>
> array = %w(a B c d Cool e f G)
> array.unshift(array.slice!(array.index(array.find { |i| i =~ /cool/i
> }))) if array.find { |i| i =~ /cool/i }
>
> Better/cleaner/shorter ways to do it?

Not sure about better or cleaner, but here's a couple of ideas:

Without error checking:

	(ary - [t = ary.detect { |$_| ~/cool/i }]).unshift(t)

If you know there's only one of each element to start with, you could
do:

	(ary.unshift(ary.detect { |$_| ~/cool/i })).uniq

still without error checking, or:

	ary.unshift(ti).uniq if ti = ary.detect { |$_| ~/cool/i }

To ensure you don't get a nil at the start, or

	ary.unshift(*ti).uniq if ti = ary.grep(/cool/i)

to ensure you don't get flamed for using $_ ;)

Of course, if you have dupes to start with this won't work.

***
DISCLAIMER
***
I am not advocating the default input space, I don't think you should
use
$_, it's only a few extra characters to do it the 'proper way', I just
wanted to go for brevity here.
***

:)
6076c22b65b36f5d75c30bdcfb2fda85?d=identicon&s=25 Ezra Zygmuntowicz (Guest)
on 2005-12-21 22:14
(Received via mailing list)
On Dec 21, 2005, at 12:08 PM, Payton Swick wrote:

>
> -Payton
>

  array.unshift(array.delete_at(array.index("Cool")))

# => ["Cool", "a", "B", "c", "d", "e", "f", "G"]

-Ezra
Cff9eed5d8099e4c2d34eae663aae87e?d=identicon&s=25 Jacob Fugal (Guest)
on 2005-12-21 22:17
(Received via mailing list)
On 12/21/05, Dan Diebolt <dandiebolt@yahoo.com> wrote:
> Try this:
>
>   array1 = %w(a B c d Cool e f G)
> array1.partition {|i| i =~ /cool/i}
> array2=array1.partition {|i| i =~ /cool/i}
> array2.flatten     #or array2.flatten!

So something like:

  array = %w(a B c d Cool e f G)
  array = array.partition{ |el| el =~ /cool/i }.flatten

That certainly would work (assuming that only one element matches the
condition), and has a certain elegance. I wonder if there's a
significant performance penalty?

It also gave me this idea:

  class Array
    def bubble_up
      score = lambda{ |a| (yield a) ? -1 : 1 }
      self.sort_by { |a,b| score[a] <=> score[b] }
    end

    def bubble_up!( &proc )
      self.replace(self.bubble_up( &proc ))
    end
  end

  array = array.bubble_up! { |el| el =~ /cool/i }

Jacob Fugal
Aee77dba395ece0a04c688b05b07cd63?d=identicon&s=25 Daniel Berger (Guest)
on 2005-12-21 22:20
(Received via mailing list)
Jacob Fugal wrote:
> > Better/cleaner/shorter ways to do it?
> >
> > -Payton
>
> array = %w(a B c d Cool e f G)
> if cool = array.find { |i| i =~ /cool/i }
>   array.delete(cool)
>   array.unshift(cool)
> end

array.unshift(array.delete("Cool"))

Regards,

Dan
82e62c756d89bc6fa0a0a2d7f2b1e617?d=identicon&s=25 Ross Bamford (Guest)
on 2005-12-21 22:38
(Received via mailing list)
On Wed, 21 Dec 2005 21:13:11 -0000, Ezra Zygmuntowicz
<ezmobius@gmail.com>
wrote:

>   array.unshift(array.delete_at(array.index("Cool")))
>
> # => ["Cool", "a", "B", "c", "d", "e", "f", "G"]
>

Nice :) I missed 'index'. It led me to this:

	([ary[ary.index("Cool")]] + ary).uniq

Then from there to

	([*ary.grep(/Cool/)] + ary).uniq

Obviously it's useless line-noise and doesn't perform well I expect, but
I
enjoy this game :)
Ad97b577f331ae29ed90da5751f2e44f?d=identicon&s=25 Dan Diebolt (dandiebolt)
on 2005-12-21 22:41
(Received via mailing list)
>That certainly would work (assuming that only one element matches the
condition),

  seems to work on multiple matches:

  array = %w(a B c d Cool e f G Cool Cooler)
=> ["a", "B", "c", "d", "Cool", "e", "f", "G", "Cool", "Cooler"]
array = array.partition{ |el| el =~ /cool/i }.flatten!
=> ["Cool", "Cool", "Cooler", "a", "B", "c", "d", "e", "f", "G"]

I was uncertain about needing parenthesis before applying flatten but it
makes perfect sense:

  array = (array.partition{ |el| el =~ /cool/i }).flatten!
Cff9eed5d8099e4c2d34eae663aae87e?d=identicon&s=25 Jacob Fugal (Guest)
on 2005-12-21 23:23
(Received via mailing list)
On 12/21/05, Dan Diebolt <dandiebolt@yahoo.com> wrote:
> >That certainly would work (assuming that only one element matches the
> condition),
>
>   seems to work on multiple matches:

Yeah, I realized that as I was working through my variation, but never
made it back up to change the text. :)

> I was uncertain about needing parenthesis before applying flatten but it makes perfect 
sense:
>
>   array = (array.partition{ |el| el =~ /cool/i }).flatten!

You don't need the bang here, however. Since Array#partition will
always create nested arrays, you won't run into the flatten! returning
nil bug, but it is still wasted effort to change a temporary array in
place.

Jacob Fugal
918c6daad03c85e51ad1a11f57017947?d=identicon&s=25 Devin Mullins (Guest)
on 2005-12-22 05:30
(Received via mailing list)
Payton Swick wrote:

> Hi,
>
> I have an array of Strings, and I'd like to find one of the items by
> Regexp, then move that item to the front of the array, eg:
>
> array = %w(a B c d Cool e f G)
> array.unshift(array.slice!(array.index(array.find { |i| i =~ /cool/i
> }))) if array.find { |i| i =~ /cool/i }
>
> Better/cleaner/shorter ways to do it?

I'm going to do something controversial and suggestion a completely
imperative method:

array.each_with_index {|o,i| array.unshift(array.delete_at(i)) if o =~
/cool/i}

Oddly, this works with multiple instances of Cool. I take it that
#each_with_index doesn't miss a beat wrt the modifications.

Oh, and if you'll be doing it more than once in your code, then put the
method where it belongs:
class Array
 def bubble_if!(regex=nil)
  if block_given? then each_with_index {|o,i| unshift(delete_at(i)) if
yield o}
  else super() {|o| =~ regex}
  end
end
end
array.bubble_if!(/cool/i)

Devin
YMMV.
A9c4658e9e475e13d790ae419acf01b6?d=identicon&s=25 =?ISO-8859-1?Q?Simon_Kr=F6ger?= (Guest)
on 2005-12-22 11:47
(Received via mailing list)
No inject solution?

here it is:

array = %w(a B c d Cool e f G)
p array.inject([]){|s, o| o =~ /cool/i ? [o, *s] : s << o}

=> ["Cool", "a", "B", "c", "d", "e", "f", "G"]

cheers

Simon
Ad97b577f331ae29ed90da5751f2e44f?d=identicon&s=25 Dan Diebolt (dandiebolt)
on 2005-12-22 12:05
(Received via mailing list)
I like it! I used to puzzle over why they named it "inject" but then I
realized that injection is like mainlining a variable through each
element of an enum. The injected variable is often used as some type of
accumulator but this convention is not necessary; the injected variable
can be used for any purpose.
5224d56434a3d237408c217605df8a56?d=identicon&s=25 Payton Swick (Guest)
on 2005-12-22 15:57
(Received via mailing list)
Just for the sake of completeness, here is Devin's suggestion, with
syntax errors corrected:

class Array
   def bubble_if!(regex=nil)
     if block_given? then each_with_index {|o,i| unshift(delete_at(i))
if yield o}
     else bubble_if! {|o| o =~ regex}
     end
   end
end

Thanks for the suggestions, everyone!

-Payton
D36eff3004b39abc4b93fe8a410d8bd3?d=identicon&s=25 Ron M (Guest)
on 2005-12-23 02:01
(Received via mailing list)
Ezra Zygmuntowicz wrote:
> On Dec 21, 2005, at 12:08 PM, Payton Swick wrote:
>>
>> array = %w(a B c d Cool e f G)
>> array.unshift(array.slice!(array.index(array.find { |i| i =~ /cool/ i
>> }))) if array.find { |i| i =~ /cool/i }
>>
>> Better/cleaner/shorter ways to do it?
>
>  array.unshift(array.delete_at(array.index("Cool")))


array.sort_by{|x| x=~/cool/i ? 0 : 1}
918c6daad03c85e51ad1a11f57017947?d=identicon&s=25 Devin Mullins (Guest)
on 2005-12-23 03:07
(Received via mailing list)
Ron M wrote:

> array.sort_by{|x| x=~/cool/i ? 0 : 1}

+1
This topic is locked and can not be replied to.