3
\$\begingroup\$

I have a dataset and I want to search task name inside initialTasks array.

const stagesTasks = [
    {
      "dataTestID": "stage-0",
      "headerText": "Backlog",
      "initialTasks": ["task 1", "task 2", "task 3"]
    },
    {
      "dataTestID": "stage-1",
      "headerText": "To Do",
      "initialTasks": ["task 4", "task 5", "task 6"]
    },
    {
      "dataTestID": "stage-2",
      "headerText": "Ongoing",
      "initialTasks": ["task 7", "task 8"]
    },
    {
      "dataTestID": "stage-3",
      "headerText": "Done",
      "initialTasks": ["task 9"]
    }
  ]

For example if I want to know dataTestID for "task 8", I have to make two loops. Like below :-

getStageName = task => {
    for(let record of stagesTasks){
      for(let data of record.initialTasks){
        if(data === task){
          return record
        }
      }
    } 
    return null
  }

Just need your guidance to identify is there any way to avoid two loops?

\$\endgroup\$
1
  • 1
    \$\begingroup\$ The current question title of your question is too generic to be helpful. Please edit to the site standard, which is for the title to simply state the task accomplished by the code. Please see How do I ask a good question?. \$\endgroup\$ – BCdotWEB Jul 27 '20 at 9:38
3
\$\begingroup\$

You could use find to get the object which includes the given task in its initialTasks array. This also uses 2 loops. But, it returns when the match is found and is less verbose.

const stagesTasks=[{dataTestID:"stage-0",headerText:"Backlog",initialTasks:["task 1","task 2","task 3"]},{dataTestID:"stage-1",headerText:"To Do",initialTasks:["task 4","task 5","task 6"]},{dataTestID:"stage-2",headerText:"Ongoing",initialTasks:["task 7","task 8"]},{dataTestID:"stage-3",headerText:"Done",initialTasks:["task 9"]}];

function getStageName(task) {
  return stagesTasks.find(s => s.initialTasks.includes(task))?.dataTestID
}

console.log(getStageName("task 2"))
console.log(getStageName("task 7"))
console.log(getStageName("doesn't exist"))

If you call this function repeatedly, then you could create a mapper object which maps each task to it's dataTestID. You can just use mapper[task] to obtain the dataTestID. Since you are returning early, I'm assuming the tasks are unique.

const stagesTasks=[{dataTestID:"stage-0",headerText:"Backlog",initialTasks:["task 1","task 2","task 3"]},{dataTestID:"stage-1",headerText:"To Do",initialTasks:["task 4","task 5","task 6"]},{dataTestID:"stage-2",headerText:"Ongoing",initialTasks:["task 7","task 8"]},{dataTestID:"stage-3",headerText:"Done",initialTasks:["task 9"]}];

const mapper = stagesTasks.reduce((acc, o) => {
  o.initialTasks.forEach(t => acc[t] = o.dataTestID)
  return acc
}, {})

function getStageName(task) {
  return mapper[task]
}

console.log(getStageName("task 2"))
console.log(getStageName("task 7"))
console.log(getStageName("task 9"))

\$\endgroup\$
1
\$\begingroup\$

You may use indexOf (Array.indexOf) to avoid nested looping.

getStageName = task => {
  for (let record of stagesTasks) {
    if (-1 === record.initialTasks.indexOf(task))
      continue
    return record
  }
  return null
}
\$\endgroup\$
1
  • \$\begingroup\$ indexOf loops the array, so this does nothing to avoid the nested looping outside of providing the optimization of an early return if value is found early in the nested indexOf search. This could be achieved by break command in traditional loop. \$\endgroup\$ – Mike Brant Jul 29 '20 at 13:21
0
\$\begingroup\$

The reality is that your data structure is forcing you to do nested loops. There is no two ways around doing nested loops unless you change your data structure.

If you want to change you data structure, you can get this two an O(n) complexity operation from it's current state of O(m x n) (where n is number of stages and m is max number of tasks in any stage).

The simplest approach might be to initialTasks from an Array to a Set. So something like...

const stagesTasks = [
  {
    "dataTestID": "stage-0",
    "headerText": "Backlog",
    "initialTasks": new Set(["task 1", "task 2", "task 3"])
  },
  {
    "dataTestID": "stage-1",
    "headerText": "To Do",
    "initialTasks": new Set(["task 4", "task 5", "task 6"])
  },
  {
    "dataTestID": "stage-2",
    "headerText": "Ongoing",
    "initialTasks": new Set(["task 7", "task 8"])
  },
  {
    "dataTestID": "stage-3",
    "headerText": "Done",
    "initialTasks": new Set(["task 9"])
  }
];

const getStageByTask = (task) => {
 return stagesTasks.find( (stage) => stage.initialTasks.has(task) ) || null;
}

A few other thoughts:

  • I am guessing that you are fairly new to development and/or javascript, so I would heavily make the recommendation to use ; to terminate your lines. In javascript, generally speaking, it is very forgiving about not using line terminators, but there are a few gotchas out there to be aware of in knowing how to write JS without ;. I would suggest you use them until you are thoroughly familiar with these cases.
  • getStageName is probably not a very good name for the function, as you are not just returning a name, but an object. Perhaps getStageByTask or similar?
  • Make sure you are using const (in this case), or let`varwhen declaring your variables. Soconst getStageName = ...`.
\$\endgroup\$

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.