title: Tokenizing/processing common grocery list input in Swift
body: <p>I'm trying to write a small function in Swift that can accept common strings someone would use to add items to a grocery/shopping list and process them to extract the key data.</p>
  
  <p>Sentences like the following, and their expected key data:</p>
  
  <ul>
  <li>"5 pounds of apples" – apples | pounds | 5</li>
  <li>"2 cans of tuna" – tuna | can | 2</li>
  <li>"Cantaloupes" – cantaloupes</li>
  <li>"A box of raisins" – raisins | box | 1</li>
  <li>"Handful of oats" – oats | handful</li>
  </ul>
  
  <p>I haven't gotten around to recognizing spelled-out numbers ("<em>Four</em> boxes") or sizing words ("4 <em>large</em> boxes") yet, but that's besides the point right now. The issue I'm having is removing the split up components from the array when they've been recognized. For example, in the string "5 pounds of apples" after "of" is removed and "apples" is up next I get an <code>Array index out of range</code>.</p>
  
  <p>Here's the full function - I've commented it a bit:</p>
  
  <pre><code>// Desired return dictionary: 'original', 'quantity', 'unit', 'item'
  
  func extractGroceryTextData(string: String) -&gt; Dictionary&lt;String, AnyObject&gt;? {
      var data: Dictionary&lt;String, AnyObject&gt; = Dictionary&lt;String, AnyObject&gt;()
  
      // Split the string by spaces
      var components = string.componentsSeparatedByString(" ")
  
      // Add the original string to the dictionary
      data["original"] = string
  
      // Array of words that can be used as measurable units
      let groceryUnits: Array&lt;String&gt; = [
          "loaf",       "loaves",
          "box",        "boxes",
          "pack",       "packs",      "packages",
          "can",        "cans",
          "container",  "containers",
          "bunch",      "bunches",
          "handful",    "handfuls",
          "cup",        "cups",
          "tablespoon", "tablespoons",
          "teaspoon",   "teaspoons",
          "bottle",     "bottles"
      ]
  
      // Array of words that we don't need
      let groceryTossWords: Array&lt;String&gt; = [
          " ", "of", "lot"
      ]
  
      // Iterate through each word and tokenize when possible
      for (index, component) in enumerate(components) {
          var string = component.lowercaseString
  
          // First word is 'a', second word is not 'lot': 1 quantity
          if index == 0 &amp;&amp; string == "a" &amp;&amp; components[1].lowercaseString != "lot" {
              data["quantity"] = 1
              components.removeAtIndex(index)
          // 'a lot' is the beginning of the string: remove
          } else if string == "a" &amp;&amp; components[index + 1] == "lot" {
              components.removeAtIndex(index)
              components.removeAtIndex(index + 1)
          // Component is found in groceryTossWords array: remove
          } else if contains(groceryTossWords, string) {
              components.removeAtIndex(index)
          // Component is found in groceryUnits array: unit
          } else if contains(groceryUnits, string) {
              data["unit"] = string
              components.removeAtIndex(index)
          // Component is a number (has a range within decimalDigitCharacterSet): quantity
          } else if string.rangeOfCharacterFromSet(NSCharacterSet.decimalDigitCharacterSet(), options: nil, range: nil) != nil {
              data["quantity"] = string
              components.removeAtIndex(index)
          }
      }
  
      // Leftover components joined together: item
      data["item"] = " ".join(components)
  
      return data
  }
  </code></pre>
  
  <p>Please bare in mind that I'm relatively new to Swift. Does anyone know how I can fix this? Better yet - is there a better way of approaching this?</p>