使用javascript进行深度优先搜索:无法获取我想要的记录

时间:2018-04-07 20:03:08

标签: javascript recursion depth-first-search

我很难理解递归是如何工作的。我有这个文字对象:

let obj = {
    pizze:{
        type:"pizze",
        typeOne:{
            title: "Pizze Rosse",
            data:[
                {id:1,type:"pizza",name:"Margherita",price:4,ingredients:["pomodoro","mozzarella"],quantity:0,inventory:100},
                {id:2,type:"pizza",name:"Marinara",price:3.5,ingredients:["aglio","pomodoro"],quantity:0,inventory:100},
                {id:3,type:"pizza",name:"Salsiccia e funghi",price:6,ingredients:["salsiccia","funghi","mozzarella","pomodoro"],quantity:0,inventory:100},
                {id:4,type:"pizza",name:"Carciofi",price:4,ingredients:["mozzarella","carciofi","pomodoro"],quantity:0,inventory:100},  
            ]
        },
        typeTwo:{
            title:"Pizze Bianche",
            data:[
                {id:5,type:"pizza",name:"Gorgonzola e noci",price:5.50,ingredients:["gorgonzola","noci","mozzarella"],quantity:0,inventory:100},
                {id:6,type:"pizza",name:"Stracchino e rucola",price:4.50,ingredients:["stracchino","rucola","basilico"],quantity:0,inventory:100},
                {id:7,type:"pizza",name:"Tartufo e salsiccia",price:8,ingredients:["tartufo","salsiccia","mozzarella"],quantity:0,inventory:100},
                {id:8,type:"pizza",name:"Zucchine e Gamberi",price:5,ingredients:["zucchine","gamberi","pomodorini"],quantity:0,inventory:100},
            ]

        }
    },
    primiPiatti:{
        type:"primiPiatti",
        typeOne:{
            title: "Primi di carne",
            data:[
                {id:12,type:"primiPiatti",name:"Lasagne alla Bolognese",price:9,quantity:0,inventory:100},
                {id:13,type:"primiPiatti",name:"Spaghetti alla carbonara",price:14,quantity:0,inventory:100},
                {id:14,type:"primiPiatti",name:"pennette all'amatriciana",price:10,quantity:0,inventory:100},
                {id:15,type:"primiPiatti",name:"paccheri salsiccia e funghi",price:12,quantity:0,inventory:100},  
            ]
        },
        typeTwo:{
            title:"Primi di pesce",
            data:[
                {id:16,type:"primiPiatti",name:"spaghetti allo scoglio",price:8.50,quantity:0,inventory:100},
                {id:17,type:"primiPiatti",name:"zuppa di cozze",price:14.50,quantity:0,inventory:100},
                {id:18,type:"primiPiatti",name:"pappardelle asparagi e gamberi",price:18,quantity:0,inventory:100},
                {id:19,type:"primiPiatti",name:"lasagne al salmone",price:15,quantity:0,inventory:100},
            ]

        },
        typeThree:{
            title:"Primi vegetariani",
            data:[
                {id:20,type:"primiPiatti",name:"Potage di cavolfiori e porri",price:15.50,quantity:0,inventory:100},
                {id:21,type:"primiPiatti",name:"lasagne con patate e taleggio",price:14.50,quantity:0,inventory:100},
                {id:22,type:"primiPiatti",name:"crespelle formaggio e zucca",price:12,quantity:0,inventory:100},
                {id:23,type:"primiPiatti",name:"cannelloni coste e noci",price:15,quantity:0,inventory:100},
            ]

        },
    },
    secondiPiatti:{
        type:"secondiPiatti",
        typeOne:{
            title: "secondi di carne",
            data:[
                {id:24,type:"secondiPiatti",name:"agnello al forno con patate",price:9,quantity:0,inventory:100},
                {id:25,type:"secondiPiatti",name:"arrosto di vitello",price:12.5,quantity:0,inventory:100},
                {id:26,type:"secondiPiatti",name:"coniglio al forno",price:11,quantity:0,inventory:100},
                {id:27,type:"secondiPiatti",name:"vitello al tonno",price:12.5,quantity:0,inventory:100},  
            ]
        },
        typeTwo:{
            title:"secondi di pesce",
            data:[
                {id:28,type:"secondiPiatti",name:"seppie con piselli",price:8.5,quantity:0,inventory:100},
                {id:29,type:"secondiPiatti",name:"orata al forno",price:14.5,quantity:0,inventory:100},
                {id:30,type:"secondiPiatti",name:"filetto di merluzzo al forno",price:18.5,quantity:0,inventory:100},
                {id:31,type:"secondiPiatti",name:"calamari ripieni",price:16,quantity:0,inventory:100},
            ]

        },
        typeThree:{
            title:"secondi vegetariani",
            data:[
                {id:32,type:"secondiPiatti",name:"parmigiana di melanzane",price:9.50,quantity:0,inventory:100},
                {id:33,type:"secondiPiatti",name:"polpette di verdure",price:7.50,quantity:0,inventory:100},
                {id:34,type:"secondiPiatti",name:"frittata di asparagi",price:7,quantity:0,inventory:100},
                {id:35,type:"secondiPiatti",name:"burger di patate",price:12,quantity:0,inventory:100},
            ]

        },

    },  

}

我想实现一个系统,它向我返回存储在数据数组中的特定记录,并通过他的ID进行检索。

现在,由于数据数组存储在树的末尾,我原本想用deepth first search

我没有使用太多的递归,所以我遇到了一些麻烦。

我的第一次尝试就是这样,我放了一个计数器,所以我可以看到会发生什么,似乎是,这是一个深度的第一次搜索:

let i = 0;

let search = (where, haystack,id,record=null) =>{
        Object.keys(haystack).forEach(key=>{ // loop over the key of the main object
        console.log("point",i,key)
        if(key === where){ //if key === data
          console.log("inside the data",i)
                    let record = haystack[key].find(rec => rec.id === id ); //find the record I want
                    if(record){ // if exist
                        return record   //return it
                    } 
                  }
       else if(typeof haystack[key] === 'object'){ // else repeat the procedure with subobject
                    return search(where, haystack[key],id,record); 
                  }  
      })
      i++
      return false
    }

console.log(search('data',obj,23))

我从'pizze'对象开始,直到数据存储在typeOne对象中,然后我转到存储在typeTwo中的数据,依此类推......

但是这种方法不起作用,我每次都得到假...我想我还不明白那里发生了什么,在递归函数中调用堆栈是如何工作的..

我的目标也是:当你找到记录时,返回并退出。这个is not possible with ForEach,所以我选择了一个不同策略的经典for循环:

    let record;

    let search2 = (where, haystack,id) =>{
    let keys = Object.keys(haystack);
    for(let i=0; i<keys.length;i++){
        if(keys[i] === where){
            record = haystack[keys[i]].find(rec => rec.id === id );
            if(record){ 
                console.log(record) // I have the record assigned 
                break
            }
        }    
       if(typeof haystack[keys[i]] === 'object'){
        search2(where, haystack[keys[i]],id,record)
        }
    }
    return false
}





    console.log(search2('data',obj,24)) // return false but is ok
    console.log("record: ",record) //undefined

正如你所看到的,我尝试在外部作用域中声明一个变量,当我在循环中找到我想要的对象时,我将它分配给记录然后II从循环中退出,但是记录仍未定义。

如果我在最内部的if内部执行console.log(),我实际上已经在控制台上打印了我想要的值。但后来似乎休息不起作用

我错过了什么?有人可以解释一下为什么我的两种方法都不起作用,最重要的是,在递归过程中会发生什么?

非常感谢

2 个答案:

答案 0 :(得分:1)

也许使用迭代器来简化它:

  function* flatPizza() {
     for(const type of ["typeOne", "typeTwo", "typeThree"])
       yield* obj.pizze[type].data.values();
  }

 for(const el of flatPizza())
   if(el.id === 23) return el;

或者在更一般的情况下:

   function* flatIterate(sth) {
     if(Array.isArray(sth)) {
          for(const el of sth)
             yield* flatIterate(el);
     } else if(typeof prop === "object") {
           yield obj;
           yield* flatIterate(Object.values(obj));
     }
  }

  for(const el of flatIterate(obj))
    if(el.id === 23) return el;

答案 1 :(得分:0)

好的,最后我认为我在第一个函数中得到了问题:

if(key === where){

     let record = haystack[key].find(rec => rec.id === id ); 
                    if(record){ 
                        return record
                    } 
                  }

记录经常被重新分配我认为,所以我需要的是跟踪结果的东西,例如数组:

let search = (where, haystack,id,found=[]) =>{
        Object.keys(haystack).forEach(key=>{ 
        if(key === where){
                    let record = haystack[key].find(rec => rec.id === id );
                    if(record){
                        found.push(record)
                        return found    //return found to the next caller
                    } 
                  }
       else if(typeof haystack[key] === 'object'){
                    return search(where, haystack[key],id,found); 
                  }  
      })

      return found[0] //finally return the record I want
    }

如果记录存在于集合中,则将其推送到找到的数组中,该数组作为参数传递给下一个调用者,但下一个调用者不会重新分配它,只需升级找到的数组并将其返回到下一个来电者。

我希望我明白第一种情况是什么问题。有人可以确认吗?

当然在这种情况下我必须遍历整个集合,但至少我想我理解为什么第一个搜索功能无法正常工作