[QUIZ] Price Ranges (#164)

Apologies for the late quiz... been rather busy today. Here's another simple
one, but practical. I didn't get specific about input
formats/parameters/etc, I leave that to you this week.

···

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

The three rules of Ruby Quiz 2:

1. Please do not post any solutions or spoiler discussion for this
quiz until 48 hours have passed from the time on this message.

2. Support Ruby Quiz 2 by submitting ideas as often as you can! (A
permanent, new website is in the works for Ruby Quiz 2. Until then,
please visit the temporary website at

     <http://splatbang.com/rubyquiz/<http://matthew.moss.googlepages.com/home>>.

3. Enjoy!

Suggestion: A [QUIZ] in the subject of emails about the problem
helps everyone on Ruby Talk follow the discussion. Please reply to
the original quiz message, if you can.
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

## Price Ranges

_Quiz description by James Edward Gray II_

You have a list of price ranges from different vendors like:

    Company A: $1,000 - $3,000
    Company B: $6,000 - $10,000
    Company C: $500 - $2,500

Given such a list and the desired price range a shopper wishes to pay,
return the companies the shopper should examine. For example, if the
shopper's price range was:

    Low: $2,500
    High: $5,000

the companies returned would be:

    Company A
    Company C

The shopper should also be allowed to provide just a low or just a high
value instead of both, should they prefer to do so.

2. Support Ruby Quiz 2 by submitting ideas as often as you can! (A
permanent, new website is in the works for Ruby Quiz 2. Until then,
please visit the temporary website at

Why does typing a simple email have to be so difficult? *sheesh* The
correct, unmanged web address is:

<http://splatbang.com/rubyquiz/&gt;

You weren't looking for a user interface, were you? :wink:

http://pastie.textmate.org/207034

Chris

···

On May 30, 6:27 pm, Matthew Moss <matthew.m...@gmail.com> wrote:

[Note: parts of this message were removed to make it a legal post.]

Apologies for the latequiz... been rather busy today. Here's another simple
one, but practical. I didn't get specific about input
formats/parameters/etc, I leave that to you this week.

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

The three rules of RubyQuiz2:

1. Please do not post any solutions or spoiler discussion for thisquizuntil 48 hours have passed from the time on this message.

2. Support RubyQuiz2 by submitting ideas as often as you can! (A
permanent, new website is in the works for RubyQuiz2. Until then,
please visit the temporary website at

     <http://splatbang.com/rubyquiz/&lt;http://matthew.moss.googlepages.com/home&gt;&gt;\.

3. Enjoy!

Suggestion: A [QUIZ] in the subject of emails about the problem
helps everyone on Ruby Talk follow the discussion. Please reply to
the originalquizmessage, if you can.
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

## Price Ranges

_Quiz description by James Edward Gray II_

You have a list of price ranges from different vendors like:

    Company A: $1,000 - $3,000
    Company B: $6,000 - $10,000
    Company C: $500 - $2,500

Given such a list and the desired price range a shopper wishes to pay,
return the companies the shopper should examine. For example, if the
shopper's price range was:

    Low: $2,500
    High: $5,000

the companies returned would be:

    Company A
    Company C

The shopper should also be allowed to provide just a low or just a high
value instead of both, should they prefer to do so.

Matthew Moss ha scritto:

Apologies for the late quiz... been rather busy today. Here's another simple
one, but practical. I didn't get specific about input
formats/parameters/etc, I leave that to you this week.

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

The three rules of Ruby Quiz 2:

1. Please do not post any solutions or spoiler discussion for this
quiz until 48 hours have passed from the time on this message.

2. Support Ruby Quiz 2 by submitting ideas as often as you can! (A
permanent, new website is in the works for Ruby Quiz 2. Until then,
please visit the temporary website at

     <http://splatbang.com/rubyquiz/&lt;http://matthew.moss.googlepages.com/home&gt;&gt;\.

3. Enjoy!

Suggestion: A [QUIZ] in the subject of emails about the problem
helps everyone on Ruby Talk follow the discussion. Please reply to
the original quiz message, if you can.
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

## Price Ranges

_Quiz description by James Edward Gray II_

You have a list of price ranges from different vendors like:

    Company A: $1,000 - $3,000
    Company B: $6,000 - $10,000
    Company C: $500 - $2,500

Given such a list and the desired price range a shopper wishes to pay,
return the companies the shopper should examine. For example, if the
shopper's price range was:

    Low: $2,500
    High: $5,000

the companies returned would be:

    Company A
    Company C

The shopper should also be allowed to provide just a low or just a high
value instead of both, should they prefer to do so.

Here my solution (with specs):

http://pastie.caboo.se/207093
http://pastie.caboo.se/207095

Bye.
Andrea

I had not created a SAX listener before in Ruby. So, my solution reads the vendor list in from XML. I noticed others were using this pastie thingy... I don't know what that is. Besides, pasties always seemed to be kind of pointless to me. They leave precious little to the imagination. So, below is the ruby file followed by the xml file:

···

---------------------------------------------------------------------------------
#!/usr/local/bin/ruby -w
require 'rexml/parsers/sax2parser'
require 'rexml/sax2listener'

class VendorListener
  include REXML::SAX2Listener

  def initialize(low, high)
    begin
      @low = Float(low)
    rescue
      @low = 0
    end
    
    begin
      @high = Float(high)
    rescue
      #if someone can spend more than this then
      #they can afford a better program
      @high = 10**15
    end
  end
  
  def start_element(uri, localname, tag_name, attrs)
    if tag_name == 'Vendor'
      @vendorName = attrs['name']
    end
  end

  def end_element(uri, localname, tag_name)
    if tag_name == 'Vendor'
      if in_range?
        puts @vendorName
      end
    elsif tag_name == 'LowPrice'
      @lowPrice = Float(@data)
    elsif tag_name == 'HighPrice'
      @highPrice = Float(@data)
    end
  end

  def characters(value)
    @data = value
  end

  def in_range?
    (@low >= @lowPrice and @low <= @highPrice) or
      (@high >= @lowPrice and @high <= @highPrice) or
      (@low < @lowPrice and @high > @highPrice)
  end
end

puts "Enter a price range."
print "Enter low value: "
low = gets
print "Enter high value: "
high = gets

parser = REXML::Parsers::SAX2Parser.new(File.new('vendors.xml' ))
parser.listen(VendorListener.new(low, high))
parser.parse

puts "Program terminated."
---------------------------------------------------------------------------------
<?xml version="1.0" encoding="UTF-8"?>
<Vendors>
    <Vendor name="Company A">
        <PriceRange>
            <LowPrice>1000</LowPrice>
            <HighPrice>3000</HighPrice>
        </PriceRange>
    </Vendor>
    <Vendor name="Company B">
        <PriceRange>
            <LowPrice>6000</LowPrice>
            <HighPrice>10000</HighPrice>
        </PriceRange>
    </Vendor>
    <Vendor name="Company C">
        <PriceRange>
            <LowPrice>500</LowPrice>
            <HighPrice>2500</HighPrice>
        </PriceRange>
    </Vendor>
</Vendors>

_________________________________________________________________
Make every e-mail and IM count. Join the i’m Initiative from Microsoft.
http://im.live.com/Messenger/IM/Join/Default.aspx?source=EML_WL_ MakeCount

Here's my solution. I use codeforpeople's main, so the program accepts
a --help that explains the params. I expected a file in CSV format for
the company data:

company1, low, high
company2, low, high

and the shopper's low and high are passed as options to the program:

require 'main'
require 'fastercsv'

Main {
  argument('file')
  
  option('low', 'l') {
    argument_required
    cast :int
    default 0
  }
  
  option('high', 'g') {
    argument_required
    cast :int
  }

  def run
    file = params[:file].value
    low = params[:low].value
    have_high = params[:high].given?
    high = params[:high].value if have_high
    values =
    FasterCSV.foreach file do |line|
      cmp_values = [line[0]]
      cmp_values << line[1].to_i
      cmp_values << line[2].to_i
      values << cmp_values
    end
    
    unless have_high
      companies = values.select {|x| x[2] >= low}
    else
      companies = values.reject {|x| (x[1] > high) || (x[2] < low)}
    end
    companies.map! {|x| x[0]}
    puts companies
  end
}

For example for this company_data.csv file:

company1, 1000, 3000
company2, 6000, 10000
company3, 500, 2500

these are some runs:

$ ruby quiz164.rb company_data.csv
company1
company2
company3

$ ruby quiz164.rb company_data.csv -l 4000
company2

$ ruby quiz164.rb company_data.csv -l 4000 -g 5000

$ ruby quiz164.rb company_data.csv -g 1500
company1
company3

$ ruby quiz164.rb company_data.csv -g 750
company3

$ ruby quiz164.rb --help
NAME
  quiz164.rb

SYNOPSIS
  quiz164.rb file [options]+

PARAMETERS
  file (1 -> file)
  --low=low, -l (0 ~> int(low=0))
  --high=high, -g (0 ~> int(high))
  --help, -h

Thanks for the quiz.

Jesus.

···

On Sat, May 31, 2008 at 2:27 AM, Matthew Moss <matthew.moss@gmail.com> wrote:

## Price Ranges

_Quiz description by James Edward Gray II_

You have a list of price ranges from different vendors like:

   Company A: $1,000 - $3,000
   Company B: $6,000 - $10,000
   Company C: $500 - $2,500

Given such a list and the desired price range a shopper wishes to pay,
return the companies the shopper should examine. For example, if the
shopper's price range was:

   Low: $2,500
   High: $5,000

the companies returned would be:

   Company A
   Company C

The shopper should also be allowed to provide just a low or just a high
value instead of both, should they prefer to do so.

A simple solution to my first Ruby Quiz

class SupplierFinder
  attr_accessor :companies
  def initialize(suppliers='')
    @companies = Hash.new
    File.open(suppliers).each do |line|
      range = line.scan(/\$[\d,]+/)
      companies.store( Range::new(parse(range[0]),parse(range[1])),
line.match(/^[\w\s]*/))
    end
  end
  def search(switch=nil, low=0, high=0)
    my_range = Range::new(low, high) if switch == '-r'
    companies.each_pair do |range, name|
      puts name if ( !my_range.nil? and (my_range.include? range.first
or my_range.include? range.last or range.include? low or
range.include? high )) or (switch == '-h' and range.first <= low) or
(switch == '-l' and range.last >= low)
    end
  end
  private
  def parse(price)
    price.gsub( '$', '').gsub(',','').to_i
  end
end
SupplierFinder.new(ARGV[0]).search(ARGV[1], ARGV[2].to_i,
ARGV[3].to_i) if __FILE__ == $0

Cheers,

Toby.

···

On May 31, 1:27 am, "Matthew Moss" <matthew.m...@gmail.com> wrote:

Apologies for the late quiz... been rather busy today. Here's another simple
one, but practical. I didn't get specific about input
formats/parameters/etc, I leave that to you this week.

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

The three rules of Ruby Quiz 2:

1. Please do not post any solutions or spoiler discussion for this
quiz until 48 hours have passed from the time on this message.

2. Support Ruby Quiz 2 by submitting ideas as often as you can! (A
permanent, new website is in the works for Ruby Quiz 2. Until then,
please visit the temporary website at

     <http://splatbang.com/rubyquiz/&lt;http://matthew.moss.googlepages.com/home&gt;&gt;\.

3. Enjoy!

Suggestion: A [QUIZ] in the subject of emails about the problem
helps everyone on Ruby Talk follow the discussion. Please reply to
the original quiz message, if you can.
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

## Price Ranges

_Quiz description by James Edward Gray II_

You have a list of price ranges from different vendors like:

    Company A: $1,000 - $3,000
    Company B: $6,000 - $10,000
    Company C: $500 - $2,500

Given such a list and the desired price range a shopper wishes to pay,
return the companies the shopper should examine. For example, if the
shopper's price range was:

    Low: $2,500
    High: $5,000

the companies returned would be:

    Company A
    Company C

The shopper should also be allowed to provide just a low or just a high
value instead of both, should they prefer to do so.

=begin

Here is my solution.
It accepts input like this:
2500_5000 #lower_upper
2500_ #lower only
_5000 #upper only

=end

min,max = ARGV[0].split(/_/)
a,b,c = (1000..3000).to_a,(6000..10000).to_a,(500..2500).to_a
l,u = min.to_i,max.to_i
u = [a,b,c].flatten.max if max == nil
l = 0 if min == ""
cust = (l..u).to_a
p "a" unless (cust & a).empty?
p "b" unless (cust & b).empty?
p "c" unless (cust & c).empty?

Harry

···

--
A Look into Japanese Ruby List in English

I hadn't planned on making another submission, but after tinkering with it a bit I realized a few things:

1. A simple GUI would be nice. I used FXRuby.
2. The SAX Listener was barfing when the number of vendors was six or more. Funny thing was that it only had a problem when the XML was formatted. If I removed the whitespace it did just fine. In any case, I switched to using a StreamListener instead.
3. There was an inconsistency in the logic in my original in_range? method.

Anyway, here is the Ruby code followed by sample XML:

···

--------------------------------------------------------------
#!/usr/local/bin/ruby -w
require 'rexml/document'
require 'rexml/streamlistener'
require 'fox16'
include Fox

class VendorListener
  include REXML::StreamListener
  def initialize(low_price, high_price, vendor_search_window)
    begin
      @low_price = Float(low_price)
    rescue
      @low_price = 0
    end
    
    begin
      @high_price = Float(high_price)
    rescue
      #if someone can spend more than this then
      #she or he can afford a better program
      @high_price = 10**15
    end
    
    @vdr_srch_wdw = vendor_search_window
  end
  
  def tag_start(name, attrs)
    if name == 'Vendor'
      @vendor_name = attrs['name']
    end
  end
  
  def tag_end(name)
    if name == 'Vendor' and
      if in_range?
        @vdr_srch_wdw.add_vendor(@vendor_name)
      end
    elsif name == 'LowPrice'
      @vendor_low_price = Float(@data)
    elsif name == 'HighPrice'
      @vendor_high_price = Float(@data)
    end
  end

  def text(text)
    @data = text
  end

  def in_range?
    @low_price <= @high_price and
      (@low_price >= @vendor_low_price or @high_price >= @vendor_low_price) and
      (@low_price <= @vendor_high_price or @high_price <= @vendor_high_price)
  end
end

class VendorSearchWindow < FXMainWindow
  def initialize(app)
    # Invoke base class initialize first
    super(app, "Ruby Quiz \#164: Vendor Search", nil, nil, DECOR_TITLE | DECOR_CLOSE)

    #Add text field frame at the top
    textfields = FXHorizontalFrame.new(self, LAYOUT_SIDE_TOP|LAYOUT_CENTER_X)
    FXLabel.new(textfields, "Enter a range:", nil, JUSTIFY_LEFT)
    FXLabel.new(textfields, "low:", nil, JUSTIFY_RIGHT)
    @low_field = FXTextField.new(textfields, 10, nil, 0, JUSTIFY_RIGHT|FRAME_SUNKEN|FRAME_THICK|LAYOUT_SIDE_TOP)
    FXLabel.new(textfields, "high:", nil, JUSTIFY_RIGHT)
    @high_field = FXTextField.new(textfields, 10, nil, 0, JUSTIFY_RIGHT|FRAME_SUNKEN|FRAME_THICK|LAYOUT_SIDE_TOP)
    
    #add button frame at the bottom
    buttons = FXHorizontalFrame.new(self, LAYOUT_SIDE_BOTTOM|LAYOUT_CENTER_X|PACK_UNIFORM_WIDTH)
    show_button = FXButton.new(buttons, "Show Vendors")
    show_button.connect(SEL_COMMAND, method(:on_show_vendors))
    exit_button = FXButton.new(buttons, "Exit")
    exit_button.connect(SEL_COMMAND, method(:on_exit))

    #Place the list in a sunken frame
    sunken_frame = FXHorizontalFrame.new(self,
            LAYOUT_FILL_X|LAYOUT_FILL_Y|FRAME_SUNKEN|FRAME_THICK, :padding => 0)
    @vendor_list = FXList.new(sunken_frame, :opts => LIST_SINGLESELECT|LAYOUT_FILL_X|LAYOUT_FILL_Y)
  end

  def on_exit(sender, sel, ptr)
    getApp().exit
  end
  
  def on_show_vendors(sender, sel, ptr)
    @vendor_list.clearItems(false)
    REXML::Document.parse_stream(File.new('vendors.xml' ),
      VendorListener.new(@low_field.text, @high_field.text, self))
  end
  
  def add_vendor(vndr_name)
    @vendor_list.appendItem(vndr_name)
  end
  
  def create
    super
    show(PLACEMENT_SCREEN)
  end
end

application = FXApp.new
VendorSearchWindow.new(application)
application.create
application.run
--------------------------------------------------------------
<?xml version="1.0" encoding="UTF-8"?>
<Vendors>
    <Vendor name="Company A">
        <PriceRange>
            <LowPrice>1000</LowPrice>
            <HighPrice>3000</HighPrice>
        </PriceRange>
    </Vendor>
    <Vendor name="Company B">
        <PriceRange>
            <LowPrice>6000</LowPrice>
            <HighPrice>10000</HighPrice>
        </PriceRange>
    </Vendor>
    <Vendor name="Company C">
        <PriceRange>
            <LowPrice>500</LowPrice>
            <HighPrice>2500</HighPrice>
        </PriceRange>
    </Vendor>
</Vendors>

_________________________________________________________________
Now you can invite friends from Facebook and other groups to join you on Windows Live™ Messenger. Add now.
https://www.invite2messenger.net/im/?source=TXT_EML_WLH_AddNow_Now

Here's my quick attempt

Cheers,
Dave

···

-----------------------------

def select_shops shops, wanted_min, wanted_max
   shops.select do |shop|
     (wanted_max ? wanted_max>= shop[:prices].min : true) &&
     (wanted_min ? wanted_min<= shop[:prices].max : true)
   end
end

shops= [{:name=> 'Company A', :prices=> 1_000.. 3_000},
         {:name=> 'Company B', :prices=> 6_000..10_000},
         {:name=> 'Company C', :prices=> 500.. 2_500}]

select_shops(shops, 2_500, 4_000).each do |shop|
   puts shop[:name]
end

Why does typing a simple email have to be so difficult? *sheesh* The
correct, unmanged web address is:

Unmanged?

*smack head*

I swear, my I.Q. must be dropping 10 points _daily_.

My simple solution:

http://pastie.caboo.se/207066

- donald

Sorry for the double post.
Short, boring reason why :slight_smile:

I assumed that input had to be taken in the way outlined in the
challenge.
So most of my code is about parsing that to create the required ranges.

···

=========================

class String
  def better_to_f
    str = match(/\D*([0-9,.]+)/)[1]
    str.gsub!(',','_')
    str.to_f
  end
end

$vendors = <<-STRING
Company A: $1,000 - $3,000
Company B: $6,000 - $10,000
Company C: $500 - $2,500
STRING

vendor_regexp = /(\w.*\w):\s*\$([0-9,.]+) - \$([0-9,.]+)/

$shopper = <<-STRING
Low: $3,500
High: $7,000
STRING

require 'yaml'
parsed = YAML.parse($shopper)
low = parsed['Low'] && parsed['Low'].value.better_to_f
high = parsed['High'] && parsed['High'].value.better_to_f

vendors = $vendors.scan(vendor_regexp)
result = []
vendors.each do |name, min, max|
  min = min.better_to_f
  max = max.better_to_f

  if low && low > max
    next
  elsif high && high < min
    next
  else
    result << name
  end
end
--
Posted via http://www.ruby-forum.com/.

Why does typing a simple email have to be so difficult? *sheesh* The
correct, unmanged web address is:

Unmanged?

*smack head*

I swear, my I.Q. must be dropping 10 points _daily_.

Cheer up Matt I am trying to put my gmail into TWI*M mode for years
now and I cannot figure out how. Hugh.
R.

···

On Sat, May 31, 2008 at 3:50 AM, Matthew Moss <matthew.moss@gmail.com> wrote:

--
http://ruby-smalltalk.blogspot.com/

---
Whereof one cannot speak, thereof one must be silent.
Ludwig Wittgenstein