1

I have a very specific requirement. There is a three or four liner solution for the same. I need one liner solution.

Let's say i have an array like:

[["Andorra", "Andorra"], ["United Arab Emirates", "United Arab Emirates"], 
 ["Afghanistan", "Afghanistan"], ["Antigua & Barbuda", "Antigua & Barbuda"], 
 ["Anguilla", "Anguilla"], ["Albania", "Albania"], ["Armenia", "Armenia"], 
 ["Angola", "Angola"]]

I want ["Anguilla", "Anguilla"] as my first element and else other in descending order.

Is there any way to achieve this ? (One liner if possible)

9
  • 9
    Why do you need a one-liner when you have a working solution? Post your solution, by the way. Commented Jan 22, 2013 at 11:36
  • 1
    @Sergio: When you have 3/4 lines of code to do a simple filter it probably means it's done with sloppy imperative code (each, inplace updates, ...). So no, no need to write one-lines, but yes, let's write concise functional code whenever possible. Commented Jan 22, 2013 at 12:09
  • 1
    btw, are the pairs in the array always the same? why? Commented Jan 22, 2013 at 12:11
  • 2
    Just replace all linebreaks with semicolons. Voilà: one-liner! Commented Jan 22, 2013 at 12:59
  • @SergioTulentsev Thanks guys for replying. The thing is i need a country list for my drop down list. To use Rails FormHelper i needed a list of countries which has US and CA on top of the list. To achieve this i don't want to have a separate helper method and crap. So, needed one liner. Commented Jan 22, 2013 at 14:02

4 Answers 4

6

Take advantage of the lexicographical order defined by arrays and sort the input by the desired criteria:

countries.sort_by { |s, s2| [s == "Anguilla" ? 1 : 0, s] }.reverse
#=> [["Anguilla", "Anguilla"], ..., ["Afghanistan", "Afghanistan"]]

@Sergio proposed using Enumerable#sort to avoid the reverse. IMO it's less declarative than the former, but for the record, that's how it may look:

countries.sort { |(s1, _), (s2, _)| s1 == "Anguilla" ? -1 : s2 <=> s1 }
#=> [["Anguilla", "Anguilla"], ..., ["Afghanistan", "Afghanistan"]]
Sign up to request clarification or add additional context in comments.

3 Comments

Good one! Better than mine :)
@SergioTulentsev: Thanks :-) It's a classic, using arrays to sort with lexicographical order. I'd like to remove that reverse, but I don't find a way, you cannot "negate" a string like an integer :-/
Check my answer for a "no reverse" solution :)
4

Here's your one-liner:

countries = [["Andorra", "Andorra"], ["United Arab Emirates", "United Arab Emirates"], ["Afghanistan", "Afghanistan"], ["Antigua & Barbuda", "Antigua & Barbuda"], ["Anguilla", "Anguilla"], ["Albania", "Albania"], ["Armenia", "Armenia"], ["Angola", "Angola"]]

custom_order = countries.partition{|arr| arr[0] == "Anguilla"}.map(&:sort).map(&:reverse).flatten(1)
custom_order # => [["Anguilla", "Anguilla"], ["United Arab Emirates", "United Arab Emirates"], ["Armenia", "Armenia"], ["Antigua & Barbuda", "Antigua & Barbuda"], ["Angola", "Angola"], ["Andorra", "Andorra"], ["Albania", "Albania"], ["Afghanistan", "Afghanistan"]]

Here is an alternative solution:

custom_order = countries.sort do |c1, c2|
  if c1[0] == 'Anguilla'
    -1
  else
    c2 <=> c1
  end
end

8 Comments

@tokland: I think it does. "Anguilla" is less than any other element, and other elements we compare in a "backwards" manner. You can check the output :)
I removed the comment, I wrote it too fast :-) But I still have doubts about comparing Anguilla with itself and the block returning -1. Maybe c1 == c2 for that branch?. Anyway, +1 :-)
@tokland: it's a block to #sort. It's supposed to return one of [-1, 0, 1] :)
you are right, let me think about it, but it should return 0 for that case.
@tokland: I don't think so. Why?
|
0

a bit primitive, but one line:

[["Anguilla", "Anguilla"]] << ([["Andorra", "Andorra"], ["United Arab Emirates", "United Arab Emirates"], ["Afghanistan", "Afghanistan"], ["Antigua & Barbuda", "Antigua & Barbuda"], ["Anguilla", "Anguilla"], ["Albania", "Albania"], ["Armenia", "Armenia"], ["Angola", "Angola"]] - ["Anguilla", "Anguilla"]).sort{|a,b| b[0] <=> a[0]}

Comments

0

Try this:

[["Andorra", "Andorra"], ["United Arab Emirates", "United Arab Emirates"], ["Afghanistan", "Afghanistan"], ["Antigua & Barbuda", "Antigua & Barbuda"], ["Anguilla", "Anguilla"], ["Albania", "Albania"], ["Armenia", "Armenia"], ["Angola", "Angola"]].sort{|a,b| b[0] <=> a[0]}.partition{|arr| arr[0] == "Anguilla"}

2 Comments

=> [[["Anguilla", "Anguilla"]], [["United Arab Emirates", "United Arab Emirates"], ["Armenia", "Armenia"], ["Antigua & Barbuda", "Antigua & Barbuda"], ["Angola", "Angola"], ["Andorra", "Andorra"], ["Albania", "Albania"], ["Afghanistan", "Afghanistan"]]] This is the output
That's what I mean. It doesn't quite match the desired output, does it?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.