"Joining" strings which may be nil (or) Handling Option hashes

Discussion in 'Ruby' started by Gavri Fernandez, Feb 13, 2005.

  1. Hi everyone,
    I want to create a query based on options passed in through
    an options hash to my method.

    The query would look like
    "title:ruby and author:dave and publisher:eek:reilly"
    or
    "title:ruby and publisher:addison-wesley"
    or
    "author:dave"
    or some other combination based on what options have been set in the hash.

    options[:author] |= "" followed by Array#join doesn't help me
    because it would lead to successive 'and's in the query.

    What is the Ruby Idiom to achieve what I want?

    TIA
    --
    Gavri
    ---------------------------------------------------
    I blog here: http://gavri.blogspot.com
    Gavri Fernandez, Feb 13, 2005
    #1
    1. Advertising

  2. "Gavri Fernandez" <> schrieb im Newsbeitrag
    news:...
    > Hi everyone,
    > I want to create a query based on options passed in through
    > an options hash to my method.
    >
    > The query would look like
    > "title:ruby and author:dave and publisher:eek:reilly"
    > or
    > "title:ruby and publisher:addison-wesley"
    > or
    > "author:dave"
    > or some other combination based on what options have been set in the hash.
    >
    > options[:author] |= "" followed by Array#join doesn't help me
    > because it would lead to successive 'and's in the query.
    >
    > What is the Ruby Idiom to achieve what I want?


    Lots of, here are some:

    >> opts = {"title"=>"ruby", "author"=>"dave", "publisher"=>"oreilly",
    >> "foo"=>nil}

    => {"title"=>"ruby", "author"=>"dave", "foo"=>nil, "publisher"=>"oreilly"}
    >> opts.select{|k,v|v}

    => [["title", "ruby"], ["author", "dave"], ["publisher", "oreilly"]]
    >> opts.select{|k,v|v}.map{|k,v| "#{k}=#{v}"}.join(" and ")

    => "title=ruby and author=dave and publisher=oreilly"

    Here's a more efficient variant - using #inject of course :)

    >> opts.inject(nil){|s,(k,v)| v ? (s ? s << " and " : "") << k << "=" << v :
    >> s}

    => "title=ruby and author=dave and publisher=oreilly"

    Kind regards

    robert
    Robert Klemme, Feb 13, 2005
    #2
    1. Advertising

  3. On Feb 13, 2005, at 11:44 AM, Robert Klemme wrote:
    > Lots of, here are some:
    >
    >>> opts = {"title"=>"ruby", "author"=>"dave", "publisher"=>"oreilly",
    >>> "foo"=>nil}

    > => {"title"=>"ruby", "author"=>"dave", "foo"=>nil,
    > "publisher"=>"oreilly"}
    >>> opts.select{|k,v|v}

    > => [["title", "ruby"], ["author", "dave"], ["publisher", "oreilly"]]
    >>> opts.select{|k,v|v}.map{|k,v| "#{k}=#{v}"}.join(" and ")

    > => "title=ruby and author=dave and publisher=oreilly"
    >
    > Here's a more efficient variant - using #inject of course :)
    >
    >>> opts.inject(nil){|s,(k,v)| v ? (s ? s << " and " : "") << k << "="
    >>> << v : s}

    > => "title=ruby and author=dave and publisher=oreilly"


    Maybe the OP isn't worried about this, but: Is there a way to represent
    ORs as well as ANDs if you're doing this?

    Francis Hwang
    http://fhwang.net/
    Francis Hwang, Feb 13, 2005
    #3
  4. On Mon, 14 Feb 2005 01:44:57 +0900, Robert Klemme <> wrote:
    >
    > "Gavri Fernandez" <> schrieb im Newsbeitrag
    > news:...


    > Lots of, here are some:
    >
    > >> opts = {"title"=>"ruby", "author"=>"dave", "publisher"=>"oreilly",
    > >> "foo"=>nil}

    > => {"title"=>"ruby", "author"=>"dave", "foo"=>nil, "publisher"=>"oreilly"}
    > >> opts.select{|k,v|v}

    > => [["title", "ruby"], ["author", "dave"], ["publisher", "oreilly"]]
    > >> opts.select{|k,v|v}.map{|k,v| "#{k}=#{v}"}.join(" and ")

    > => "title=ruby and author=dave and publisher=oreilly"
    >
    > Here's a more efficient variant - using #inject of course :)
    >
    > >> opts.inject(nil){|s,(k,v)| v ? (s ? s << " and " : "") << k << "=" << v :
    > >> s}

    > => "title=ruby and author=dave and publisher=oreilly"



    This is the solution I hit upon right after I sent my mail. Please
    critique while I try to understand your solutions :)

    def get_query(options)
    query_fragments = []
    options.each do |key, value|
    query_fragments.push("#{key.id2name}:#{value}")
    end
    query = query_fragments.join(" and ")
    end

    Thanks Robert
    --
    Gavri
    ---------------------------------------------------
    I blog here: http://gavri.blogspot.com
    Gavri Fernandez, Feb 13, 2005
    #4
  5. "Gavri Fernandez" <> schrieb im Newsbeitrag
    news:...
    > On Mon, 14 Feb 2005 01:44:57 +0900, Robert Klemme <>
    > wrote:
    >>
    >> "Gavri Fernandez" <> schrieb im Newsbeitrag
    >> news:...

    >
    >> Lots of, here are some:
    >>
    >> >> opts = {"title"=>"ruby", "author"=>"dave", "publisher"=>"oreilly",
    >> >> "foo"=>nil}

    >> => {"title"=>"ruby", "author"=>"dave", "foo"=>nil,
    >> "publisher"=>"oreilly"}
    >> >> opts.select{|k,v|v}

    >> => [["title", "ruby"], ["author", "dave"], ["publisher", "oreilly"]]
    >> >> opts.select{|k,v|v}.map{|k,v| "#{k}=#{v}"}.join(" and ")

    >> => "title=ruby and author=dave and publisher=oreilly"
    >>
    >> Here's a more efficient variant - using #inject of course :)
    >>
    >> >> opts.inject(nil){|s,(k,v)| v ? (s ? s << " and " : "") << k << "=" <<
    >> >> v :
    >> >> s}

    >> => "title=ruby and author=dave and publisher=oreilly"

    >
    >
    > This is the solution I hit upon right after I sent my mail. Please
    > critique while I try to understand your solutions :)
    >
    > def get_query(options)
    > query_fragments = []
    > options.each do |key, value|
    > query_fragments.push("#{key.id2name}:#{value}")
    > end
    > query = query_fragments.join(" and ")
    > end


    I'm missing the condition. As far as I understood you you want to be able
    to skip nil values. Did I get this wrong? Apart from that it does
    certainly what you want. If you want to spare the intermediate array you
    can do a bit optimization:

    def get_query(options)
    q = nil
    options.each do |k,v|
    q = (q ? q << " and " : "") << k << ":" << v
    end
    q
    end

    This is basically a verbose variant of my inject version.

    > Thanks Robert


    You're welcome!

    Kind regards

    robert
    Robert Klemme, Feb 13, 2005
    #5
  6. "Francis Hwang" <> schrieb im Newsbeitrag
    news:...
    > On Feb 13, 2005, at 11:44 AM, Robert Klemme wrote:
    >> Lots of, here are some:
    >>
    >>>> opts = {"title"=>"ruby", "author"=>"dave", "publisher"=>"oreilly",
    >>>> "foo"=>nil}

    >> => {"title"=>"ruby", "author"=>"dave", "foo"=>nil,
    >> "publisher"=>"oreilly"}
    >>>> opts.select{|k,v|v}

    >> => [["title", "ruby"], ["author", "dave"], ["publisher", "oreilly"]]
    >>>> opts.select{|k,v|v}.map{|k,v| "#{k}=#{v}"}.join(" and ")

    >> => "title=ruby and author=dave and publisher=oreilly"
    >>
    >> Here's a more efficient variant - using #inject of course :)
    >>
    >>>> opts.inject(nil){|s,(k,v)| v ? (s ? s << " and " : "") << k << "=" << v
    >>>> : s}

    >> => "title=ruby and author=dave and publisher=oreilly"

    >
    > Maybe the OP isn't worried about this, but: Is there a way to represent
    > ORs as well as ANDs if you're doing this?


    You could provide multiple values for a key and create an OR from that:

    opts = {"title"=>["ruby","foobar"], "author"=>"dave"}

    opts.inject(nil) do |s,(k,v)|
    if v
    (s ? s << " and " : "") <<
    (Enumerable === v && ! (String === v) ?
    v.inject(nil) {|s2,v2| (s2 ? s2 << " or " : "(") << k << ":" << v2} <<
    ")" :
    "#{k}:#{v}")
    else
    s
    end
    end

    Slightly unreadable... :)) You don't need the outer if-else-end though if
    v is never nil.

    Kind regards

    robert
    Robert Klemme, Feb 13, 2005
    #6
  7. Robert Klemme wrote:
    >
    > Here's a more efficient variant - using #inject of course :)
    >
    > >> opts.inject(nil){|s,(k,v)| v ? (s ? s << " and " : "") << k << "="

    << v :
    > >> s}

    > => "title=ruby and author=dave and publisher=oreilly"



    I prefer this:

    opts.to_a.map{|x| x.join('=') if x[1]}.compact.join(' and ')

    ----> "title=ruby and author=dave and publisher=oreilly"
    William James, Feb 13, 2005
    #7
  8. On Mon, 14 Feb 2005 02:35:02 +0900, Robert Klemme <> wrote:

    > I'm missing the condition. As far as I understood you you want to be able
    > to skip nil values. Did I get this wrong? Apart from that it does


    My bad. My mail was cluttered with
    1) The problem
    2) Possible solution by handling unused options by not considering them.
    3) Possible solution by setting unused options as nil and handling
    them while "joining"

    Anyway, thanks for the solutions.


    --
    Gavri
    ---------------------------------------------------
    I blog here: http://gavri.blogspot.com
    Gavri Fernandez, Feb 14, 2005
    #8
  9. William James wrote:
    > Robert Klemme wrote:
    > >
    > > Here's a more efficient variant - using #inject of course :)
    > >
    > > >> opts.inject(nil){|s,(k,v)| v ? (s ? s << " and " : "") << k <<

    "="
    > << v :
    > > >> s}

    > > => "title=ruby and author=dave and publisher=oreilly"

    >
    >
    > I prefer this:
    >
    > opts.to_a.map{|x| x.join('=') if x[1]}.compact.join(' and ')
    >
    > ----> "title=ruby and author=dave and publisher=oreilly"


    Too prolix. Better:

    opts.map{|x| x.join('=') if x[1]}.compact.join(' and ')

    Here are the stages through which the data travels:

    {"title"=>"ruby", "author"=>"dave", "foo"=>nil, "publisher"=>"oreilly"}
    ["title=ruby", "author=dave", nil, "publisher=oreilly"]
    ["title=ruby", "author=dave", "publisher=oreilly"]
    "title=ruby and author=dave and publisher=oreilly"
    William James, Feb 15, 2005
    #9
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Brian Candler

    puts nil generates "nil\n"

    Brian Candler, Nov 6, 2004, in forum: Ruby
    Replies:
    1
    Views:
    111
  2. John Carter
    Replies:
    64
    Views:
    640
    Klaus Stein
    May 19, 2005
  3. Christoffer Sawicki
    Replies:
    5
    Views:
    254
    Christoffer Sawicki
    Sep 2, 2006
  4. Edward Wijaya

    Joining 2 arrays into hashes

    Edward Wijaya, Jun 1, 2004, in forum: Perl Misc
    Replies:
    20
    Views:
    236
    Ben Morrow
    Jun 4, 2004
  5. Tim O'Donovan

    Hash of hashes, of hashes, of arrays of hashes

    Tim O'Donovan, Oct 27, 2005, in forum: Perl Misc
    Replies:
    5
    Views:
    212
Loading...

Share This Page