Grain Delivers Piping Hot, Healthy Food to You in 15 Minutes

As reported by Terence Lee at Tech in Asia on 15 August 2014. More details can be found here.

Healthy food isn’t readily available in Asia. Brought up on cheap local fare and McDonalds, Asians won’t change their diet easily. The lack of demand, coupled with the relatively smaller number of health-conscious eateries, heighten the difficulty of healthy living.
Singapore startup Grain is turning this into an opportunity. Think of it as an Uber for wholesome food: it serves up ready-to-eat dishes to a taxi stand near to you, and it promises to do it on-demand.
“The moment you think about food, we want to get it to your doorstep within 15 minutes,” says Yi Sung Yong, one of the co-founders of the startup. Typical food catering services take much longer.
Grain is a young operation. It only began delivering at the end of 2013, stopped for a couple of months, and then kickstarted it again in February 2014.
They’ve tried different things. Initially, you’d tell Grain your preferences and plot out a week ahead which days you’d like your food, but that approach made operations difficult since they had to cook many variations on the same day.
Customers also found it inconvenient since schedules often change. The new on-demand service is designed to better meet their needs while making the business easier to run.
/** * helper function to take the form and * get back a js object with field/value mappings * ignoring submit fields and fields whose name we can't ascertain */ function serialize(form){ return .filter(isInputElement) .filter(isNotSubmit) .filter(hasFieldName) .reduce(function(data, node, index) { data[getFieldName(node)] = node.value return data; }, {}); } /** * Check if this form element is of type input */ function isInputElement(node) { return node.tagName.toLowerCase() === 'input'; } /** * Check if this is a 'submit' input element */ function isNotSubmit(node) { return node.type !== 'submit'; } /** * Ensure field has a derived name */ function hasFieldName(node) { return getFieldName(node) !== 'unknown'; } /** * Attempt to ascertain "name" of field. * * they don't give inputs real `name` attributes, so * best guess is to hack around the element's label text, * falling back to the placeholder text. (their html is * not semantic and often labels erroneously reference * parent divs instead of sibling input elements) * * strip out the stars they show when validation fails * * if label is misplaced _and_ there's no placeholder, * this function is insufficient */ function getFieldName(input) { if (!input.labels.length) return input.placeholder || 'unknown'; var labelText = input.labels[0].textContent.trim(); return ~labelText.indexOf(' *') ? labelText.slice(0, labelText.indexOf(' *')) : labelText; } /** * Listen for form submissions and send identify * if you'd like an event too, just delete the '//' in the track line */ document.forms[0].addEventListener('submit', function(e) { analytics.identify(serialize(; // analytics.track('Submitted Form'); });