Skip to content

Instantly share code, notes, and snippets.

@grodowski
Last active August 18, 2024 16:14
Show Gist options
  • Save grodowski/8f4ac8c32e639f03e4bccddc577eac58 to your computer and use it in GitHub Desktop.
Save grodowski/8f4ac8c32e639f03e4bccddc577eac58 to your computer and use it in GitHub Desktop.
Shopify package matcher
# 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
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