Last active
August 18, 2024 16:14
-
-
Save grodowski/8f4ac8c32e639f03e4bccddc577eac58 to your computer and use it in GitHub Desktop.
Shopify package matcher
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Package matcher | |
# Description | |
# As the owner of an online store, you need to fulfill orders everyday. To optimize the packing of each order, you decide to write an algorithm to match boxes and items based on their respective sizes. | |
# You have access to the following two boxes: | |
# - A medium box (identifier: M) | |
# - A large box (identifier: L) | |
# When possible, you should try to fit multiple items in the same box but boxes can only contain one type of product. | |
# This is the list of items you sell along with associated boxes: | |
# - Camera (identifier: Cam): one can fit in a medium box, and up to two can fit in a large box | |
# - Gaming Console (identifier: Game): too big for a medium box, but up to two can fit in a large box | |
# - max of 2 g consoles can fit in 1 box | |
# - Bluetooth speaker (identifier: Blue): one can fit in a large box . max is 1 per large box | |
# Your goal is to design a function that takes a list of items and returns the box & item matches (examples below). | |
# Your solution should work for any number of each item greater than or equal to zero. | |
# Input = [], Output = [] | |
# ## Input/Output expectations | |
# ["Cam"] -> [M: ["Cam"]] | |
# ["Cam", "Game"] -> [M: ["Cam"], L: ["Game"]] | |
# ["Game", "Blue"] -> [L: ["Game"], L : ["Blue"]] | |
# ["Game", "Game", "Blue"] -> [L: ["Game", "Game"], L : ["Blue"]] | |
# ["Cam", "Cam", "Game", "Game"] -> [L: ["Cam", "Cam"], L: ["Game", "Game"]] | |
# ["Cam", "Cam", "Cam", "Game", "Game", "Game", "Cam", "Blue"] -> | |
# [L: ["Cam", "Cam"], L: ["Cam", "Cam"], L: ["Game", "Game"], L: ["Game"], L: ["Blue"]] | |
# ["Cam", "Cam", "Cam", "Game", "Game", "Cam", "Cam", "Blue", "Blue"] -> [L: ["Cam", "Cam"] , L: ["Cam", "Cam"] , M: ["Cam"] , L: ["Game", "Game"] , L: ["Blue"] , L: ["Blue"]] | |
class Box | |
BOX_CONFIG = { | |
Cam: { L: 2, M: 1 }, | |
Game: { L: 2 }, | |
Blue: { L: 1 }, | |
} | |
attr_reader :items, :size_name | |
def initialize(size_name) | |
raise ArgumentError unless %i[M L].include?(size_name.to_sym) | |
@size_name = size_name | |
@items = [] | |
end | |
def add(item) | |
raise ArgumentError if full? | |
items << item | |
end | |
def accepts?(item) | |
items.empty? || (item.to_sym == item_type && !full?) | |
end | |
def full? | |
return false if items.empty? | |
size = BOX_CONFIG[item_type][size_name] | |
items.length == size | |
end | |
private | |
def item_type | |
items.first.to_sym | |
end | |
end | |
# Finds smallest possible box for `item` | |
# we need to look at the whole list here to be smarter | |
def new_box_for(item, remaining) | |
size_name, _ = if remaining.include?(item) | |
:L | |
else | |
Box::BOX_CONFIG[item.to_sym].sort_by(&:last).first | |
end | |
return Box.new(size_name).tap { _1.add(item) } | |
end | |
def package(items) | |
boxes = [] | |
while items.size.nonzero? do | |
item = items.shift | |
candidate_box = boxes.find { _1.accepts?(item) } | |
if candidate_box | |
candidate_box.add(item) | |
else | |
boxes << new_box_for(item, items) | |
end | |
end | |
return boxes | |
end | |
# Example: | |
# [3] 3.2.2 > package ["Cam", "Cam", "Cam", "Game", "Game", "Game", "Cam", "Blue"] | |
# [ | |
# [0] #<Box:0x00007f973af9c0a8 @size_name=:L, @items=["Cam", "Cam"]>, | |
# [1] #<Box:0x00007f973af9bf68 @size_name=:L, @items=["Cam", "Cam"]>, | |
# [2] #<Box:0x00007f973af9bea0 @size_name=:L, @items=["Game", "Game"]>, | |
# [3] #<Box:0x00007f973af9bcc0 @size_name=:L, @items=["Game"]>, | |
# [4] #<Box:0x00007f973af9bae0 @size_name=:L, @items=["Blue"]> | |
# ] | |
# TODO: format output as expected |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Box = Struct.new(:size, :items) do | |
def to_s | |
"#{size}: #{items}" | |
end | |
end | |
def package(items) | |
boxes = [] | |
cam = items.select { _1 == "Cam" } | |
game = items.select { _1 == "Game" } | |
blue = items.select { _1 == "Blue" } | |
cam.each_slice(2) do |cams| | |
size = cams.size > 1 ? :L : :M | |
boxes << Box.new(size, cams) | |
end | |
game.each_slice(2) do |games| | |
boxes << Box.new(:L, games) | |
end | |
blue.each_slice(1) do |blue| | |
boxes << Box.new(:L, blue) | |
end | |
return "[" + boxes.map(&:to_s).join(", ") + "]" | |
end | |
# Example | |
# [2] 3.2.2 > puts package ["Cam", "Cam", "Cam", "Game", "Game", "Game", "Cam", "Blue"] | |
# [L: ["Cam", "Cam"], L: ["Cam", "Cam"], L: ["Game", "Game"], L: ["Game"], L: ["Blue"]] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment