How can I optimize memory usage in my product bundling script?

How can I optimize memory usage in my product bundling script?

renanvsouza
Shopify Partner
3 0 1

Hi everyone!

I have a script running on a client to identify product bundles using tags and apply discounts. Recently, I've been getting A LOT of MemoryQuotaExceeded errors. Despite those errors, the script is working normally. Given that I'm not a Ruby expert (to say the least), could I improve the memory usage of this script? Any help is appreciated.

The code is the following:

 

products_in_cart = Input.cart.line_items
products_with_tag = {}
variants_with_discount = []

# Step 1: Group products by their tags
products_in_cart.each do |line_item|
  variant = line_item.variant
  product = variant.product

  product.tags.each do |tag|
    if tag.start_with?('bundle')
      key = tag # Use the tag as the key for grouping
      products_with_tag[key] ||= []
      products_with_tag[key] << {
        id: product.id,
        variant: variant.id,
        quantity: line_item.quantity,
        tag: tag
      }
      break # If one tag matches, move to the next product
    end
  end
end

unless products_with_tag.empty?
  
  # Step 2: Identify bundles
  products_with_tag.each do |_tag, products_in_bundle|
    # Create a hash where the keys are the tags and the values are arrays of product IDs
    products_by_id = products_in_bundle.group_by { |item| item[:id] }
  
    # Flatten the array of products
    flattened_products = products_by_id.values.flatten(1)
  
    # Pair them up
    paired_products = []
    flattened_products.each_slice(2) { |pair| paired_products << pair }
  
    # If there's an odd number of products, remove the last one
    paired_products.pop if paired_products.last.size % 2 != 0
  
    # Now we have all possible pairs of products with the same tag
    paired_products.each do |pair|
    
      # Check if both elements of each pair have the same quantity
      if pair[0][:quantity] != pair[1][:quantity]
        # Identify the product with the largest and smallest quantity
        max_product = pair.max_by { |product| product[:quantity] }
        min_product = pair.min_by { |product| product[:quantity] }
  
        # Calculate the difference as the result of the largest quantity divided by the smallest quantity
        difference = max_product[:quantity] / min_product[:quantity]
  
        # Add a property with that difference to the object with the largest quantity
        max_product[:difference] = difference
      end
  
      # Create a new object with the variant, quantity, and difference (if any) from each pair
      new_object = pair.map { |product| { variant: product[:variant], quantity: product[:quantity], difference: product[:difference] } }
    
      # Add the new object to the array of all new objects
      variants_with_discount << new_object
    
    end
  end
  
  # Step 3: Add discounts
  products_in_cart.each do |line_item|
    variant = line_item.variant
    
    # Find the corresponding new object for this variant
    var = variants_with_discount.flatten.find { |obj| obj[:variant] == variant.id }
  
    # If a new object was found and it has a difference, adjust the discount
    if var && var[:difference]
      calc = 0.25 / var[:difference]
      discount = 1 - calc
      message = 'Bundle'
      line_item.change_line_price(line_item.line_price * discount, message: message)
    elsif var
      discount = 0.75
      message = 'Bundle'
      line_item.change_line_price(line_item.line_price * discount, message: message)
    end
  
  end

end

Output.cart = Input.cart

 

 

Replies 0 (0)