简体   繁体   English

需要帮助编写 function

[英]Need help writing a function

I need some help writing a function for an app I'm creating.我需要一些帮助来为我正在创建的应用程序编写 function。

Suppose I have an array of 5 objects.假设我有一个包含 5 个对象的数组。

var pool = [
    {"color":"Pink"},
    {"color":"Pink"},
    {"color":"Green"},
    {"color":"Orange"},
    {"color":"Orange"}
];

I also have an array that defines tasks to fulfill.我还有一个定义要完成的任务的数组。

var tasks = [
    [
        {
            "amount": 1,
            "color": "Orange"
        },
        {
            "amount": 1,
            "color": "Pink"
        }
    ],
    [
        {
            "amount": 2,
            "color": "Green"
        },
        {
            "amount": 1,
            "color": "Orange"
        }
    ],
    [
        {
            "amount": 1,
            "color": "Orange"
        }
    ]
];

The tasks array contains 3 arrays.任务数组包含 3 个 arrays。 Those arrays represent the following tasks:这些 arrays 代表以下任务:

  1. Remove 1 Pink object or 1 Orange object from the pool.从池中移除 1 个粉色 object 或 1 个橙色 object。
  2. Remove 2 Green objects or 1 Orange object from the pool.从池中移除 2 个绿色对象或 1 个橙色 object。
  3. Remove 1 Orange object from the pool.从池中移除 1 个橙色 object。

I need a function that can determine if I have enough objects to complete all 3 tasks.我需要一个 function 可以确定我是否有足够的对象来完成所有 3 个任务。 The tasks must be done in sequence.任务必须按顺序完成。

So for the first task, we would check if we have either 1 Pink object or 1 Orange object in the pool.因此,对于第一个任务,我们将检查池中是否有 1 个粉色 object 或 1 个橙色 object。 We have 2 Pink objects and 2 Orange objects in the pool, so we know we can complete this task.我们在池中有 2 个粉色对象和 2 个橙色对象,所以我们知道我们可以完成这个任务。

For the second task, we check if we have 2 Green objects or 1 Orange object to remove from the pool.对于第二个任务,我们检查是否有 2 个绿色对象或 1 个橙色 object 要从池中删除。 We only have 1 Green object, so removing 2 Green objects is not an option.我们只有 1 个绿色 object,因此不能选择删除 2 个绿色对象。 Our only option is to remove 1 Orange object from the pool.我们唯一的选择是从池中移除 1 个橙色 object。 We considered removing one of the Orange objects for the first task, but that task can still be completed if we remove 1 of the Orange objects for this task.我们考虑为第一个任务删除一个橙色对象,但如果我们为此任务删除一个橙色对象,该任务仍然可以完成。

For the third task, we check if we have 1 Orange object to remove.对于第三个任务,我们检查是否有 1 个橙色 object 需要移除。 We have 2, but we know the second task must remove 1 of the Orange objects.我们有 2 个,但我们知道第二个任务必须删除 1 个橙色对象。 This also means we cannot remove an Orange object for the first task.这也意味着我们不能为第一个任务删除橙色 object。

So in the end, we know that the only objects we can remove for the first task are either of the 2 Pink objects.所以最后,我们知道我们可以为第一个任务删除的唯一对象是 2 个 Pink 对象中的任何一个。 The second task must remove 1 of the Orange objects, and the third must remove the other Orange object.第二个任务必须移除其中一个 Orange 对象,第三个任务必须移除另一个 Orange object。

I need to write a function that can determine all of that logic before attempting to complete any of the tasks.在尝试完成任何任务之前,我需要编写一个 function 可以确定所有逻辑。

The following function canDoTasks does not quite work, because it does not take into account whether a previous task needs to use any of the objects.下面的 function canDoTasks 不太行,因为它没有考虑前面的任务是否需要使用任何对象。

function canDoTasks1() {
    outerloop:
    for (var i = 0; i < tasks.length; i++) {
        var mandatory_task = tasks[i];
        for (var j = 0; j < mandatory_task.length; j++) {
            var optional_task = mandatory_task[j];
            var amount = countRemainingObjects(pool, optional_task.color);
            if (amount >= optional_task.amount) {
                continue outerloop;
            }
        }
        return false;
    }
    return true;
}

function countRemainingObjects(arr, color) {
    var total = 0;
    for (var i = 0; i < arr.length; i++) {
        if (arr[i].color == color) {
            total++;
        }
    }
    return total;
}

This next function canDoTasks2 also does not quite work, because it does not do a smart enough job at determining which objects need to be removed for each task.下一个 function canDoTasks2 也不能很好地工作,因为它在确定每个任务需要删除哪些对象方面做得不够聪明。

function canDoTasks2() {
    var pool_copy = pool.slice();
    outerloop:
    for (var i = 0; i < tasks.length; i++) {
        var mandatory_task = tasks[i];
        for (var j = 0; j < mandatory_task.length; j++) {
            var optional_task = mandatory_task[j];
            var amount = countRemainingObjects(pool_copy, optional_task.color);
            if (amount >= optional_task.amount) {
                removeFromPool(pool_copy, optional_task.color);
                continue outerloop;
            }
        }
        return false;
    }
    return true;
}

function removeFromPool(arr, color) {
    for (var i = 0; i < arr.length; i++) {
        if (arr[i].color == color) {
            arr.splice(i, 1);
            return;
        }
    }
}

Performing a depth-first search is one solution.执行深度优先搜索是一种解决方案。

In the function findTheWay() we re-arrange the pool into a dictionary telling us how many of each color is currently available.在 function findTheWay()中,我们将池重新排列到字典中,告诉我们每种颜色当前可用的数量。 We then use that to do a depth-first search of the tasks list to find the first way that they can all be completed.然后,我们使用它对任务列表进行深度优先搜索,以找到可以完成所有任务的第一种方式。

findTheWay() returns an array of indexes into the steps in the task list. findTheWay()返回任务列表中步骤的索引数组。

 const pool = [ {"color":"Pink"}, {"color":"Pink"}, {"color":"Green"}, {"color":"Orange"}, {"color":"Orange"} ]; const tasks = [ [ { "amount": 1, "color": "Orange" }, { "amount": 1, "color": "Pink" } ], [ { "amount": 2, "color": "Green" }, { "amount": 1, "color": "Orange" } ], [ { "amount": 1, "color": "Orange" } ] ]; const findTheWay = (items, tasks) => { // Collate the items into a color:count dictionary const pool = {}; // Only care about the items that could be handled by the task list tasks.forEach(t => t.forEach(c => c.color in pool? null: pool[c.color] = 0)); // Count up the items items.forEach(i => i.color in pool? pool[i.color]++: null); // Prep a choices array that matches up with the tasks array const choice = new Array(tasks.length).fill(0); var idx = 0; // Perform the depth-first search while(true) { // If we've run out of options for a particular step, rewind to the // previous step and choose its next option. If we rewind past the // beginning of the list, there are no solutions. if(choice[idx] >= tasks[idx].length) { if(--idx < 0) break; const subtask = tasks[idx][choice[idx]]; pool[subtask.color] += subtask.amount; choice[idx]++; continue; } // If the current choice in the current step is feasible, move to // the next step. If we move past the end of the list, we've found // a solution. const subtask = tasks[idx][choice[idx]]; if(pool[subtask.color] >= subtask.amount) { pool[subtask.color] -= subtask.amount; if(++idx >= choice.length) break; choice[idx] = 0; continue; } // This choice for this step didn't work out so move on to the // next option. choice[idx]++; } if(idx < 0) return null; return choice; }; const thisIsTheWay = findTheWay(pool, tasks); console.log(thisIsTheWay); console.log(thisIsTheWay.map((v,i) => tasks[i][v]));


Iterator/Iterable迭代器/可迭代

You asked for code that finds all solutions as an add-on to your original question.您要求找到所有解决方案的代码作为原始问题的附加组件。 Doing this requires some relatively minor adjustments.这样做需要一些相对较小的调整。

Below, I've converted the above solution to an iterator/iterable.下面,我将上述解决方案转换为迭代器/可迭代对象。 (More can be read about these in MDN's Iterators and generators and Iteration protocols .) (可以在 MDN 的迭代器和生成器以及迭代协议中了解更多信息。)

Note that the time to find all solutions increases very quickly as the number of tasks and options increases.请注意,随着任务和选项数量的增加,找到所有解决方案的时间会迅速增加。 For example, adding one task with three options triples the time required.例如,添加一项具有三个选项的任务会使所需时间增加三倍。

Note that the particular example set you provided only has the one solution.请注意,您提供的特定示例集只有一个解决方案。

 const pool = [ {"color":"Pink"}, {"color":"Pink"}, {"color":"Green"}, {"color":"Orange"}, {"color":"Orange"} ]; const tasks = [ [ { "amount": 1, "color": "Orange" }, { "amount": 1, "color": "Pink" } ], [ { "amount": 2, "color": "Green" }, { "amount": 1, "color": "Orange" } ], [ { "amount": 1, "color": "Orange" } ] ]; const theWays = (items, tasks) => { // Collate the items into a color:count dictionary const pool = {}; // Only care about the items that could be handled by the task list tasks.forEach(t => t.forEach(c => c.color in pool? null: pool[c.color] = 0)); // Count up the items items.forEach(i => i.color in pool? pool[i.color]++: null); // Prep a choices array that matches up with the tasks array const choice = new Array(tasks.length).fill(0); var idx = 0; var isDone = false; // Create an iterator/iterable object const iter = { next: () => { if(isDone) return { done: true }; // Perform the depth-first search while(true) { // If we've run out of options for a particular step, rewind to the // previous step and choose its next option. If we rewind past the // beginning of the list, we've found all the solutions. if(choice[idx] >= tasks[idx].length) { if(--idx < 0) { isDone = true; return { done: true }; }; const subtask = tasks[idx][choice[idx]]; pool[subtask.color] += subtask.amount; choice[idx]++; continue; } // If the current choice in the current step is feasible, move to // the next step. If we move past the end of the list, we've found // a solution. const subtask = tasks[idx][choice[idx]]; if(pool[subtask.color] >= subtask.amount) { pool[subtask.color] -= subtask.amount; if(++idx >= choice.length) { // The choice state is the solution. Make a copy and return that // while also selecting next option for the next loop. const solution = Array.from(choice); choice[--idx]++; return { value: solution, done: false }; } choice[idx] = 0; continue; } // This choice for this step didn't work out so move on to the // next option. choice[idx]++; } }, [Symbol.iterator]: () => iter, }; return iter; }; // Find first solution const findTheWay = (items, tasks) => { const results = theWays(items, tasks).next(); return results.done? null: results.value; } // Find all solutions const findTheWays = (items, tasks) => Array.from(theWays(items, tasks)); // Demos const thisIsTheWay = findTheWay(pool, tasks); console.log('First solution'); console.log(thisIsTheWay); console.log(thisIsTheWay.map((v,i) => tasks[i][v])); const theseAreTheWays = findTheWays(pool, tasks); console.log(`All solutions: ${theseAreTheWays.length} found.`); console.log(theseAreTheWays); console.log(theseAreTheWays.map(v => v.map((v,i) => tasks[i][v])));

we can include an array to track what needs to be deleted in each task我们可以包含一个数组来跟踪每个任务中需要删除的内容

 const removeFromPool = (pool, task, removed) => { const cachePool = [].concat(pool); const color = task.color; let amount = task.amount; for (let i = pool.length - 1; i >= 0; --i) { const item = pool[i]; if (item.color === color) { pool.splice(i, 1); amount--; if (amount === 0) { removed.push(task); return true; } } } // restore pool pool.splice(0, pool.length); cachePool.forEach(p => pool.push(p)); return false; } const canCompleteTask = (tasks, pool, removed, index = 0) => { const cachePool = [].concat(pool); const task = tasks[index]; if (task === undefined) { // completed all tasks return true; } for (let i = 0; i < task.length; i++) { const taskItem = task[i]; if (removeFromPool(cachePool, taskItem, removed) && canCompleteTask(tasks, cachePool, removed, index + 1)) { return true; } } return false; } const toRemove = [] console.log(canCompleteTask([ [{ "amount": 1, "color": "Orange" }, { "amount": 1, "color": "Pink" } ], [{ "amount": 2, "color": "Green" }, { "amount": 1, "color": "Green" } ], [{ "amount": 1, "color": "Orange" }] ], [{ "color": "Pink" }, { "color": "Pink" }, { "color": "Green" }, { "color": "Orange" }, { "color": "Orange" } ], toRemove)); console.log(toRemove);

For now I could only come up with a solution to check if all tasks combined are possible, using all options - not picking one option.现在我只能想出一个解决方案来检查是否所有的任务组合起来都是可能的,使用所有选项——而不是选择一个选项。 So this is just half a solution.所以这只是解决方案的一半。

Using up all tasks & options用完所有任务和选项

function isJobPossible (inputPool, inputTask) {
  
  // First I would transform the pool into a object, telling me how many of each color are available
  let inPool = {};
  for (let element of inputPool) {
    if (inPool[element.color]) {
      inPool[element.color] == inPool[element.color] + 1;
    } else {
      inPool[element.color] = 1;
    }
  }
 
  // Then I would sum up all colors needed from the tasks
  let inTask= {};
  for (let group in tasks) {
    for (let task in group) {
      if (inTask[task.color]) {
        inTask[task.color] = inTask[task.color] + task.amount;
      } else {
        inTask[task.color] = task.amount;
      }
    }
  }

  function isAvailableInPool (color, amount) {
    if (!inPool[color]) { return false }
    return inPool[color] >= amount
  }

  // now check if all tasks can be fullfilled with all options
  for (let color of Object.keys(inTask)) {
    let isPossible = isAvailbleInPool( color , inTask[color] ); 
    if (!isPossible) { return false }
  }

  return true

}

console.log( isJobPossible(pool, tasks) );

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM