4
\$\begingroup\$

Having the following array of objects of ingredients used by cooks

images = [{id:1, cook:"jim",   ingredients:"chicken salsa mayo ketchup tomato"},
          {id:2, cook:"steve", ingredients:"pasta mozzarela chicken"},
          {id:3, cook:"jim",   ingredients:"bread lettuce tomato onions"},
          {id:4, cook:"jim",   ingredients:"ketchup sausage bread"},
          {id:5, cook:"jim",   ingredients:"avocado ground_meat chili"},
          {id:6, cook:"jim",   ingredients:"ground_meat guacamole tomato parsley"},
          {id:7, cook:"steve", ingredients:"chicken tomato broccoli"}]

I have to extract an array of ingredients used by a specific cook so that each ingredient appears only once, return only 25 ingredients unless a specific limit is provided.

extractIngredients(array, cook, limit=25)

e.g: extractIngredients(images, "jim", 10) ==> ["chicken", "salsa", "mayo", "ketchup", "tomato", "bread", "lettuce", "onions", "hotdog", "avocado"]

BONUS POINTS:

Return an ingredient array sorted by the number of occurrences, and then alphabetically, showing the most popular ingredients first. e.g: extractIngredients(images, "jim", 10) ==> ["tomato", "bread", "ground_meat", "ketchup", "avocado", "chicken", "chili", "guacamole", "lettuce", "mayo"]

My solution:

extractIngredients(images, username, limit){
  return [...new Set(images.filter(images => images.cook == username)
       .map(a => a.keywords.split(" ")).flat())].slice(0, limit);
}

My solution has an issue with an extremely large input as it would still process all of the objects and then slice the resulting array. It would be better to just process objects until the limit is reached.

\$\endgroup\$
4
\$\begingroup\$

Sounds like a perfect case for generator functions. Unfortunately, map and the other array default methods don't have lazy versions of map/filter, but these functions aren't hard to write.

const images = [
    {id:1, cook:"jim",   ingredients:"chicken salsa mayo ketchup tomato"},
    {id:2, cook:"steve", ingredients:"pasta mozzarela chicken"},
    {id:3, cook:"jim",   ingredients:"bread lettuce tomato onions"},
    {id:4, cook:"jim",   ingredients:"ketchup sausage bread"},
    {id:5, cook:"jim",   ingredients:"avocado ground_meat chili"},
    {id:6, cook:"jim",   ingredients:"ground_meat guacamole tomato parsley"},
    {id:7, cook:"steve", ingredients:"chicken tomato broccoli"}
]

function* lazyMap(iterator,callback) {
    for (const value of iterator) {
        yield callback(value)
    }
}

function* lazyFilter(iterator,callback) {
    for (const value of iterator) {
        console.log(`looking at order with id=${value.id}`)
        if (callback(value)) yield value
    }
}

function* lazyFlat(iterator) {
    for (const list of iterator) {
        for (const value of list) {
            yield value
        }
    }
}

function* lazyUnique(iterator) {
    const seen = new Set()
    for (const value of iterator) {
        if (!seen.has(value)) yield value
        seen.add(value)
    }
}

function takeSome(iterator,limit) {
    let list = []
    for (const value of iterator) {
        list.push(value)
        if (list.length>=limit) return list
    }
    return list
}

function extractIngredients(orders,cook,limit=25) {
    const cook_orders = lazyFilter(orders,order=>order.cook===cook)
    const orders_ingredients = lazyMap(cook_orders,order=>order.ingredients.split(' '))
    const all_ingredients = lazyFlat(orders_ingredients)
    const unique_ingredients = lazyUnique(all_ingredients)
    return takeSome(unique_ingredients,limit)
}

console.log(extractIngredients(images,'jim',10))

As you can see from the console output, only IDs up to 5 are looked at, because on the order with ID=5, the 10th ingredient is found and the function returns.

The bonus seems confusing, as if your goal is to sort by frequency, you must traverse all of your data to build an accurate histogram.

| improve this answer | |
\$\endgroup\$
  • 1
    \$\begingroup\$ This is a great approach. Yes, I've been thinking about that bonus, it would require to sort first by frequency and then remove the duplicates prior to working on the array, It's not necessary, specially if sorting would greatly affect the performance. \$\endgroup\$ – Nelo Jul 12 at 22:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.