seat_selector 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Gemfile +3 -0
- data/README.md +28 -0
- data/bin/seat_selector +2 -0
- data/lib/seat_selector/alpha_converter.rb +19 -0
- data/lib/seat_selector/finder.rb +66 -0
- data/lib/seat_selector/parser.rb +31 -0
- data/lib/seat_selector/seat.rb +24 -0
- data/lib/seat_selector/venue.rb +22 -0
- data/lib/seat_selector/version.rb +3 -0
- data/lib/seat_selector.rb +8 -0
- metadata +69 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 660dbba0c80ddff35e3ecffff39d389ec5482380708579cae4f31fa4b7e95cd5
|
|
4
|
+
data.tar.gz: 16f95312509d2ab32b94bc29b3dbe347107fd3534e8ca312a892e3475050fa3c
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: ac5ba4d66ab5f3c1b0ab0ee28a4b3a82b2f9b2ab10100929026882ffcfb5e7c344cf2a8733663e97cbd441c21dc3e4669d1b8401f0063015bad721fca8c6c42d
|
|
7
|
+
data.tar.gz: 99fffe2a6012f39d88813a5eef11e6151918c6c3d1c5983e29b973585a4113e33043454a4b3ac64a03b165d7f1c8ce8eee663c04af6349c406965e08f82fa21d
|
data/Gemfile
ADDED
data/README.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Seat Selector
|
|
2
|
+
A simple Ruby gem example.
|
|
3
|
+
|
|
4
|
+
Given a JSON input representing the available seats at a public venue, 'seat_selector' finds the best available seat(s) (meaning closest to front row, center-stage). If more than one seat is requested, 'seat_selector' finds the best available group of adjacent seats (within the same row).
|
|
5
|
+
|
|
6
|
+
## Installation
|
|
7
|
+
To install:
|
|
8
|
+
```
|
|
9
|
+
$ gem install seat_selector
|
|
10
|
+
```
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```ruby
|
|
14
|
+
require 'seat_selector'
|
|
15
|
+
# Pass your JSON-formatted data to SeatSelector.
|
|
16
|
+
# You'll get back an array of the best available group of adjacent seats,
|
|
17
|
+
# or an empty array if no such group is available.
|
|
18
|
+
# See ./spec/fixtures/valid_data.json for examples of the supported format.
|
|
19
|
+
finder = SeatSelector.parse(json_str)
|
|
20
|
+
|
|
21
|
+
# request a single seat
|
|
22
|
+
seats = finder.get_best_seats(1)
|
|
23
|
+
# => [ <SeatSelector::Seat> ]
|
|
24
|
+
|
|
25
|
+
# request multiple seats
|
|
26
|
+
seats = finder.get_best_seats(3)
|
|
27
|
+
```
|
|
28
|
+
|
data/bin/seat_selector
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module SeatSelector
|
|
2
|
+
class AlphaConverter
|
|
3
|
+
# A -> 1; C -> 3; AA -> 27; AAA -> 703
|
|
4
|
+
def self.to_i(alpha_str)
|
|
5
|
+
alpha_str.each_char.inject(0) do |sum, c|
|
|
6
|
+
(sum * 26) + self.letter_values[c.downcase]
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def self.letter_values
|
|
11
|
+
@@letter_values ||= begin
|
|
12
|
+
("a".."z").each_with_object({}).with_index do |(chr, value_map), i|
|
|
13
|
+
value_map[chr] = i + 1
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
private_class_method :letter_values
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
module SeatSelector
|
|
2
|
+
class Finder
|
|
3
|
+
attr_reader :seats
|
|
4
|
+
|
|
5
|
+
def initialize(venue)
|
|
6
|
+
@seats = venue.available_seats
|
|
7
|
+
set_distance!(venue.total_columns)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def get_best_seats(seats_needed = 1)
|
|
11
|
+
return [] if seats_needed < 1
|
|
12
|
+
best_seat = nil
|
|
13
|
+
best_distance = nil
|
|
14
|
+
@seats.each do |r, seats_in_row| # skip rows with no open seats
|
|
15
|
+
# search the current row
|
|
16
|
+
seats_in_row.each do |c, seat|
|
|
17
|
+
if seats_needed == 1
|
|
18
|
+
if best_seat.nil? || best_seat.distance > seat.distance
|
|
19
|
+
best_seat = seat
|
|
20
|
+
best_distance = seat.distance
|
|
21
|
+
end
|
|
22
|
+
else
|
|
23
|
+
# check rightward from current seat
|
|
24
|
+
valid_group = (c + 1).upto(c + seats_needed - 1).all?{|i| seats_in_row.key?(i) }
|
|
25
|
+
if valid_group
|
|
26
|
+
distance = (c).upto(c + seats_needed - 1).inject(0) do |sum, i|
|
|
27
|
+
sum + seats_in_row[i].distance
|
|
28
|
+
end
|
|
29
|
+
if best_seat.nil? || best_distance > distance
|
|
30
|
+
best_seat = seat
|
|
31
|
+
best_distance = distance
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
return [] if best_seat.nil?
|
|
39
|
+
|
|
40
|
+
if seats_needed > 1
|
|
41
|
+
seats_needed.times.map {|i| @seats[best_seat.row][best_seat.column + i] }
|
|
42
|
+
else
|
|
43
|
+
[best_seat]
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
def set_distance!(total_columns)
|
|
50
|
+
median_column = median(total_columns)
|
|
51
|
+
|
|
52
|
+
@seats.each_value do |seats_in_row|
|
|
53
|
+
seats_in_row.each_value {|seat| seat.set_distance!(median_column) }
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# get median of contiguous integer range 1 to max
|
|
58
|
+
def median(max)
|
|
59
|
+
if max.even?
|
|
60
|
+
max / 2
|
|
61
|
+
else
|
|
62
|
+
(max + 1) / 2
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
|
|
3
|
+
module SeatSelector
|
|
4
|
+
class Parser
|
|
5
|
+
class UnprocessableEntity < StandardError
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def self.parse(json_str)
|
|
9
|
+
begin
|
|
10
|
+
obj = JSON.parse(json_str)
|
|
11
|
+
seats = build_available_seats(obj.fetch("seats").values)
|
|
12
|
+
layout = obj.fetch("venue").fetch("layout")
|
|
13
|
+
rows = layout.fetch("rows")
|
|
14
|
+
columns = layout.fetch("columns")
|
|
15
|
+
|
|
16
|
+
venue = Venue.new(seats, rows, columns)
|
|
17
|
+
Finder.new(venue)
|
|
18
|
+
|
|
19
|
+
rescue StandardError
|
|
20
|
+
raise UnprocessableEntity
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.build_available_seats(seats_args)
|
|
25
|
+
seats_args.map { |args| Seat.new(args) }
|
|
26
|
+
end
|
|
27
|
+
private_class_method :build_available_seats
|
|
28
|
+
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module SeatSelector
|
|
2
|
+
class Seat
|
|
3
|
+
attr_reader :row, :column, :id, :distance
|
|
4
|
+
|
|
5
|
+
def initialize(params, converter = AlphaConverter)
|
|
6
|
+
@row = converter.to_i(params.fetch("row"))
|
|
7
|
+
@column = params.fetch("column")
|
|
8
|
+
@id = params.fetch("id")
|
|
9
|
+
@is_available = (params.fetch("status") == "AVAILABLE")
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def to_s
|
|
13
|
+
@id.to_s.upcase
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def available?
|
|
17
|
+
@is_available
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def set_distance!(median_column)
|
|
21
|
+
@distance = (@row - 1).abs + (@column - median_column).abs
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module SeatSelector
|
|
2
|
+
class Venue
|
|
3
|
+
attr_reader :total_rows, :total_columns, :available_seats
|
|
4
|
+
|
|
5
|
+
def initialize(all_seats, total_rows, total_columns)
|
|
6
|
+
@total_rows = total_rows
|
|
7
|
+
@total_columns = total_columns
|
|
8
|
+
@available_seats = build_available_seats(all_seats)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
private
|
|
12
|
+
|
|
13
|
+
def build_available_seats(all_seats)
|
|
14
|
+
all_seats.each_with_object({}) do |s, seats|
|
|
15
|
+
if s.available?
|
|
16
|
+
seats[s.row] ||= {}
|
|
17
|
+
seats[s.row][s.column] = s
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: seat_selector
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Steve Lovell
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2021-08-24 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: rspec
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '3.10'
|
|
20
|
+
type: :development
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '3.10'
|
|
27
|
+
description: A simple practice app demonstrating how to make a gem.
|
|
28
|
+
email:
|
|
29
|
+
- sjlovell34@gmail.com
|
|
30
|
+
executables:
|
|
31
|
+
- seat_selector
|
|
32
|
+
extensions: []
|
|
33
|
+
extra_rdoc_files:
|
|
34
|
+
- README.md
|
|
35
|
+
files:
|
|
36
|
+
- Gemfile
|
|
37
|
+
- README.md
|
|
38
|
+
- bin/seat_selector
|
|
39
|
+
- lib/seat_selector.rb
|
|
40
|
+
- lib/seat_selector/alpha_converter.rb
|
|
41
|
+
- lib/seat_selector/finder.rb
|
|
42
|
+
- lib/seat_selector/parser.rb
|
|
43
|
+
- lib/seat_selector/seat.rb
|
|
44
|
+
- lib/seat_selector/venue.rb
|
|
45
|
+
- lib/seat_selector/version.rb
|
|
46
|
+
homepage: https://github.com/stephenjlovell/seat_selector
|
|
47
|
+
licenses:
|
|
48
|
+
- MIT
|
|
49
|
+
metadata: {}
|
|
50
|
+
post_install_message:
|
|
51
|
+
rdoc_options: []
|
|
52
|
+
require_paths:
|
|
53
|
+
- lib
|
|
54
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
55
|
+
requirements:
|
|
56
|
+
- - ">="
|
|
57
|
+
- !ruby/object:Gem::Version
|
|
58
|
+
version: 2.7.3
|
|
59
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
60
|
+
requirements:
|
|
61
|
+
- - ">="
|
|
62
|
+
- !ruby/object:Gem::Version
|
|
63
|
+
version: '0'
|
|
64
|
+
requirements: []
|
|
65
|
+
rubygems_version: 3.1.6
|
|
66
|
+
signing_key:
|
|
67
|
+
specification_version: 4
|
|
68
|
+
summary: Finds the best seat(s) at a given venue.
|
|
69
|
+
test_files: []
|