best_seats 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 5d701a3236e71ffb1c6ad63ee7fabb7c2ab311dd6d7448bafc0b274b407da693
4
+ data.tar.gz: 22174f0b2b6952a57775b87f1ad009ddc034d4c3e5b4eb6394ad2483e96f9bb3
5
+ SHA512:
6
+ metadata.gz: 8c5b7a3b2f63ea4084d8d8f3e52dae475c11d2e02b1d735af3d11bcec26a19fd7298ef42071b3e5f1e149c6c1da58d794d61d4301f378b086a37dc807dd3fd04
7
+ data.tar.gz: bffee3c5b7c474aa114f27d871360c352450d6ea7d3661c4a43afd0c153121dd6434665634f566f01ef433f82018ee1d9701583eccaf5bddf50a77ce315ee93d
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
@@ -0,0 +1 @@
1
+ ruby 2.6.3
@@ -0,0 +1,6 @@
1
+ ---
2
+ language: ruby
3
+ cache: bundler
4
+ rvm:
5
+ - 2.4.6
6
+ before_install: gem install bundler -v 2.1.4
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in best_seats.gemspec
4
+ gemspec
5
+
6
+ gem "rake", "~> 12.0"
7
+ gem "rspec", "~> 3.0"
8
+ gem "simplecov", "~> 0.18.5"
9
+ gem "pry-byebug", "~> 3.9"
@@ -0,0 +1,50 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ best_seats (0.1.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ byebug (11.1.3)
10
+ coderay (1.1.2)
11
+ diff-lcs (1.3)
12
+ docile (1.3.2)
13
+ method_source (1.0.0)
14
+ pry (0.13.1)
15
+ coderay (~> 1.1)
16
+ method_source (~> 1.0)
17
+ pry-byebug (3.9.0)
18
+ byebug (~> 11.0)
19
+ pry (~> 0.13.0)
20
+ rake (12.3.3)
21
+ rspec (3.9.0)
22
+ rspec-core (~> 3.9.0)
23
+ rspec-expectations (~> 3.9.0)
24
+ rspec-mocks (~> 3.9.0)
25
+ rspec-core (3.9.2)
26
+ rspec-support (~> 3.9.3)
27
+ rspec-expectations (3.9.2)
28
+ diff-lcs (>= 1.2.0, < 2.0)
29
+ rspec-support (~> 3.9.0)
30
+ rspec-mocks (3.9.1)
31
+ diff-lcs (>= 1.2.0, < 2.0)
32
+ rspec-support (~> 3.9.0)
33
+ rspec-support (3.9.3)
34
+ simplecov (0.18.5)
35
+ docile (~> 1.1)
36
+ simplecov-html (~> 0.11)
37
+ simplecov-html (0.12.2)
38
+
39
+ PLATFORMS
40
+ ruby
41
+
42
+ DEPENDENCIES
43
+ best_seats!
44
+ pry-byebug (~> 3.9)
45
+ rake (~> 12.0)
46
+ rspec (~> 3.0)
47
+ simplecov (~> 0.18.5)
48
+
49
+ BUNDLED WITH
50
+ 2.1.4
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2020 Marcle Rodrigues
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,168 @@
1
+ # BestSeats
2
+
3
+ Find the best seats from a list of open seats from a given venue.
4
+
5
+
6
+ ## Challenge
7
+
8
+ Write a solution to return the best seat (closest to the front & middle) given a list of open seats. Rows follow alphabetical order with "a" being the first row. Columns follow numerical order from left to right.
9
+
10
+ The list of open seats, number of rows and columns (seats) is based on a JSON input.
11
+
12
+ ```json
13
+ {
14
+ "venue": {
15
+ "layout": {
16
+ "rows": 10,
17
+ "columns": 50
18
+ }
19
+ },
20
+ "seats": {
21
+ "a1": {
22
+ "id": "a1",
23
+ "row": "a",
24
+ "column": 1,
25
+ "status": "AVAILABLE"
26
+ },
27
+ "b5": {
28
+ "id": "b5",
29
+ "row": "b",
30
+ "column": 5,
31
+ "status": "AVAILABLE"
32
+ },
33
+ "h7": {
34
+ "id": "h7",
35
+ "row": "h",
36
+ "column": 7,
37
+ "status": "AVAILABLE"
38
+ }
39
+ }
40
+ }
41
+ ```
42
+
43
+ The solution should find the best open seat (closest to the front & middle) given the input JSON and number of requested seats. Imagine a concert, people want to be as close as possible to the stage.
44
+
45
+ For example, for a venue with 10 rows and 12 columns with all seats open, the best seat would be A6.
46
+
47
+ If a group of seats is requested, the algorithm needs to find the best open group of seats together. In the example above, for 3 seats, it would be A5, A6, and A7.
48
+
49
+ For 5 columns and 2 requested seats the best open seats - assuming the first row A is fully occupied and the second row B is fully open, would be B2 and B3.
50
+
51
+ ## Installation
52
+
53
+ Add this line to your application's Gemfile:
54
+
55
+ ```ruby
56
+ gem 'best_seats'
57
+ ```
58
+
59
+ And then execute:
60
+
61
+ $ bundle install
62
+
63
+ Or install it yourself as:
64
+
65
+ $ gem install best_seats
66
+
67
+ ## Usage
68
+
69
+ The algorithm works either with a JSON or a Ruby Hash.
70
+
71
+ ```ruby
72
+ require "best_seats/finder"
73
+
74
+ file = File.read("file_path")
75
+ input = JSON.parse(file)
76
+ seats_requested = 1
77
+
78
+ finder = BestSeats::Finder.new(input, seats_requested)
79
+ finder.all
80
+
81
+ # => [:a1]
82
+ ```
83
+ ```ruby
84
+ require "best_seats/finder"
85
+
86
+ input = {
87
+ "venue": {
88
+ "layout": {
89
+ "rows": 10,
90
+ "columns": 5
91
+ }
92
+ },
93
+ "seats": {
94
+ "a1": {
95
+ "id": "a1",
96
+ "row": "a",
97
+ "column": 1,
98
+ "status": "AVAILABLE"
99
+ },
100
+ "b5": {
101
+ "id": "b5",
102
+ "row": "b",
103
+ "column": 5,
104
+ "status": "AVAILABLE"
105
+ },
106
+ "h7": {
107
+ "id": "h7",
108
+ "row": "h",
109
+ "column": 7,
110
+ "status": "AVAILABLE"
111
+ }
112
+ }
113
+ }
114
+ seats_requested = 1
115
+
116
+ finder = BestSeats::Finder.new(input, seats_requested)
117
+ finder.all
118
+
119
+ # => [:a1]
120
+ ```
121
+
122
+ You can also pass a hash of options to customize the behavior:
123
+ ```ruby
124
+ require "best_seats/finder"
125
+
126
+ file = File.read("file_path")
127
+ input = JSON.parse(file)
128
+ seats_requested = 1
129
+ options = {
130
+ index_finder: MyCustomIndexFinder
131
+ }
132
+
133
+ finder = BestSeats::Finder.new(input, seats_requested, options)
134
+ finder.all
135
+
136
+ # => [:a1]
137
+ ```
138
+ Available options:
139
+ ```ruby
140
+ DEFAULT_OPTIONS = {
141
+ matrix_builder: BestSeats::Matrix,
142
+ index_finder: BestSeats::BestSeatIndexFinder,
143
+ venue_builder: BestSeats::Venue,
144
+ seat_group: BestSeats::SeatGroup
145
+ }.freeze
146
+ ```
147
+
148
+ `matrix_builder` - Customize how to build the available seats matrix, check the original class to see the required interface.
149
+
150
+ `index_finder` - Customize how to find the best seat index, check the original class to see the required interface.
151
+
152
+ `venue_builder` - Builds the venues from the JSON input, this way you can accept other input formats. Check the original class to the required interface.
153
+
154
+ `seat_group` - Find if seats are consecutive, check the orignal class to see the required interface.
155
+
156
+ ## Development
157
+
158
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
159
+
160
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
161
+
162
+ ## Contributing
163
+
164
+ Bug reports and pull requests are welcome on GitHub at https://github.com/marclerodrigues/best_seats.
165
+
166
+ ## License
167
+
168
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,29 @@
1
+ require_relative 'lib/best_seats/version'
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = "best_seats"
5
+ spec.version = BestSeats::VERSION
6
+ spec.authors = ["Marcle Rodrigues"]
7
+ spec.email = ["maarclee@gmail.com"]
8
+
9
+ spec.summary = %q{Find the best available seat given a list of open seats for a given venue.}
10
+ spec.description = %q{Find the best available seat given a list of open seats for a given venue.}
11
+ spec.homepage = "https://github.com/marclerodrigues"
12
+ spec.license = "MIT"
13
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
14
+
15
+ # spec.metadata["allowed_push_host"] = "https://best_seats"
16
+
17
+ spec.metadata["homepage_uri"] = spec.homepage
18
+ spec.metadata["source_code_uri"] = "https://github.com/marclerodrigues/best_seats"
19
+ spec.metadata["changelog_uri"] = "https://github.com/marclerodrigues/best_seats"
20
+
21
+ # Specify which files should be added to the gem when it is released.
22
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
23
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
24
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
25
+ end
26
+ spec.bindir = "exe"
27
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
28
+ spec.require_paths = ["lib"]
29
+ end
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "best_seats"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,6 @@
1
+ require "best_seats/version"
2
+
3
+ module BestSeats
4
+ class Error < StandardError; end
5
+ # Your code goes here...
6
+ end
@@ -0,0 +1,21 @@
1
+ module BestSeats
2
+ class BestSeatIndexFinder
3
+ DENOMINATOR = 2.0
4
+
5
+ attr_reader :collection_size
6
+
7
+ def initialize(collection_size)
8
+ @collection_size = collection_size
9
+ end
10
+
11
+ def call
12
+ ((middle_position.floor + middle_position.ceil) / DENOMINATOR).to_i
13
+ end
14
+
15
+ private
16
+
17
+ def middle_position
18
+ @_middle_position ||= (collection_size - 1) / DENOMINATOR
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,100 @@
1
+ require "forwardable"
2
+ require "ostruct"
3
+ require "best_seats/matrix"
4
+ require "best_seats/best_seat_index_finder"
5
+ require "best_seats/seat_group"
6
+ require "best_seats/venue"
7
+
8
+ module BestSeats
9
+ class Finder
10
+ extend Forwardable
11
+
12
+ DEFAULT_OPTIONS = {
13
+ matrix_builder: BestSeats::Matrix,
14
+ index_finder: BestSeats::BestSeatIndexFinder,
15
+ venue_builder: BestSeats::Venue,
16
+ seat_group: BestSeats::SeatGroup
17
+ }.freeze
18
+ INITIAL_SEAT_INDEX = 0
19
+
20
+ attr_reader :input, :seats_requested
21
+
22
+ def_delegators :venue,
23
+ :rows,
24
+ :columns,
25
+ :seats
26
+ def_delegators :options,
27
+ :matrix_builder,
28
+ :index_finder,
29
+ :venue_builder,
30
+ :seat_group
31
+
32
+ def initialize(input, seats_requested, options = {})
33
+ @input = input
34
+ @seats_requested = seats_requested
35
+ @options = DEFAULT_OPTIONS.merge(options)
36
+ end
37
+
38
+ def all
39
+ selected_seats = []
40
+
41
+ available_seats_matrix.each do |line|
42
+ next if insuficient_seats?(line.size)
43
+
44
+ sorted_seats = selected_seats.sort
45
+
46
+ return sorted_seats if all_seats_found?(sorted_seats)
47
+
48
+ selected_seats = []
49
+
50
+ (INITIAL_SEAT_INDEX...seats_requested).each do |seat|
51
+ index = index_to_remove(line.size)
52
+ value = line.delete_at(index)
53
+ selected_seats << value
54
+ end
55
+ end
56
+
57
+ selected_seats.sort
58
+ end
59
+
60
+ private
61
+
62
+ def available_seats_matrix
63
+ @_available_seats_matrix ||= matrix_builder.new(
64
+ rows,
65
+ columns,
66
+ seats
67
+ ).available
68
+ end
69
+
70
+ def insuficient_seats?(total)
71
+ total < seats_requested
72
+ end
73
+
74
+ def all_seats_found?(selected_seats)
75
+ enough_selected?(selected_seats.size) && consecutive_values?(selected_seats)
76
+ end
77
+
78
+ def enough_selected?(selected_count)
79
+ selected_count == seats_requested
80
+ end
81
+
82
+ def consecutive_values?(seats)
83
+ seat_group.new(
84
+ seats.sort
85
+ ).consecutive_values?
86
+ end
87
+
88
+ def index_to_remove(collection_size)
89
+ index_finder.new(collection_size).call
90
+ end
91
+
92
+ def venue
93
+ @_venue ||= venue_builder.new(input)
94
+ end
95
+
96
+ def options
97
+ @_options ||= OpenStruct.new(@options)
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,20 @@
1
+ module BestSeats
2
+ module Helpers
3
+ class Hash
4
+ def self.deep_symbolize_keys(hash)
5
+ new.deep_symbolize_keys(hash)
6
+ end
7
+
8
+ def deep_symbolize_keys(hash)
9
+ hash.inject({}) do |memo,(key, value)|
10
+ if value.is_a?(::Hash)
11
+ memo[key.to_sym] = deep_symbolize_keys(value)
12
+ else
13
+ memo[key.to_sym] = value
14
+ end
15
+ memo
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,37 @@
1
+ module BestSeats
2
+ class Matrix
3
+ INITIAL_ROW = "a"
4
+ INITIAL_ROW_INDEX = 0
5
+ INITIAL_COLUMN_INDEX = 1
6
+
7
+ attr_reader :rows, :columns, :seats
8
+
9
+ def initialize(rows, columns, seats)
10
+ @rows = rows
11
+ @columns = columns
12
+ @seats = seats
13
+ end
14
+
15
+ def available
16
+ return @_available if defined?(@_available)
17
+
18
+ @_available = all.map do |row|
19
+ row.select { |column| seats.keys.include?(column) }
20
+ end.delete_if(&:empty?)
21
+ end
22
+
23
+ def all
24
+ return @_full if defined?(@_full)
25
+
26
+ row_letter = INITIAL_ROW
27
+
28
+ @_full = (INITIAL_ROW_INDEX...rows).map.with_index do |row, index|
29
+ row_letter = index.zero? ? INITIAL_ROW : row_letter.next
30
+
31
+ (INITIAL_COLUMN_INDEX..columns).map do |column|
32
+ "#{row_letter}#{column}".to_sym
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,35 @@
1
+ module BestSeats
2
+ class SeatGroup
3
+ attr_reader :sorted_seats
4
+
5
+ def initialize(sorted_seats)
6
+ @sorted_seats = sorted_seats
7
+ end
8
+
9
+ def consecutive_values?
10
+ sorted_seats.delete_if.with_index do |value, index|
11
+ if collection_end?(index)
12
+ true
13
+ else
14
+ consecutive_value?(value, sorted_seats[index + 1])
15
+ end
16
+ end
17
+
18
+ sorted_seats.empty?
19
+ end
20
+
21
+ private
22
+
23
+ def consecutive_value?(value, next_value)
24
+ value.next == next_value
25
+ end
26
+
27
+ def collection_end?(index)
28
+ index >= last_index
29
+ end
30
+
31
+ def last_index
32
+ sorted_seats.size - 1
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,31 @@
1
+ require "best_seats/helpers/hash"
2
+
3
+ module BestSeats
4
+ class Venue
5
+ def initialize(params)
6
+ @params = params
7
+ end
8
+
9
+ def rows
10
+ @_rows ||= layout.dig(:rows)
11
+ end
12
+
13
+ def columns
14
+ @_column ||= layout.dig(:columns)
15
+ end
16
+
17
+ def seats
18
+ @_seats ||= params.dig(:seats)
19
+ end
20
+
21
+ private
22
+
23
+ def layout
24
+ @_layout ||= params.dig(:venue, :layout)
25
+ end
26
+
27
+ def params
28
+ @_params ||= ::BestSeats::Helpers::Hash.deep_symbolize_keys(@params)
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,3 @@
1
+ module BestSeats
2
+ VERSION = "0.1.0"
3
+ end
metadata ADDED
@@ -0,0 +1,66 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: best_seats
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Marcle Rodrigues
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2020-05-23 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Find the best available seat given a list of open seats for a given venue.
14
+ email:
15
+ - maarclee@gmail.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - ".gitignore"
21
+ - ".rspec"
22
+ - ".tool-versions"
23
+ - ".travis.yml"
24
+ - Gemfile
25
+ - Gemfile.lock
26
+ - LICENSE.txt
27
+ - README.md
28
+ - Rakefile
29
+ - best_seats.gemspec
30
+ - bin/console
31
+ - bin/setup
32
+ - lib/best_seats.rb
33
+ - lib/best_seats/best_seat_index_finder.rb
34
+ - lib/best_seats/finder.rb
35
+ - lib/best_seats/helpers/hash.rb
36
+ - lib/best_seats/matrix.rb
37
+ - lib/best_seats/seat_group.rb
38
+ - lib/best_seats/venue.rb
39
+ - lib/best_seats/version.rb
40
+ homepage: https://github.com/marclerodrigues
41
+ licenses:
42
+ - MIT
43
+ metadata:
44
+ homepage_uri: https://github.com/marclerodrigues
45
+ source_code_uri: https://github.com/marclerodrigues/best_seats
46
+ changelog_uri: https://github.com/marclerodrigues/best_seats
47
+ post_install_message:
48
+ rdoc_options: []
49
+ require_paths:
50
+ - lib
51
+ required_ruby_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: 2.3.0
56
+ required_rubygems_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ requirements: []
62
+ rubygems_version: 3.0.3
63
+ signing_key:
64
+ specification_version: 4
65
+ summary: Find the best available seat given a list of open seats for a given venue.
66
+ test_files: []