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?
-
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– BCdotWEB2020年07月27日 09:38:38 +00:00Commented Jul 27, 2020 at 9:38
3 Answers 3
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"))
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
}
-
\$\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 nestedindexOf
search. This could be achieved bybreak
command in traditional loop. \$\endgroup\$Mike Brant– Mike Brant2020年07月29日 13:21:14 +00:00Commented Jul 29, 2020 at 13:21
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. PerhapsgetStageByTask
or similar?- Make sure you are using
const
(in this case), orlet
`varwhen declaring your variables. So
const getStageName = ...`.