当你不知道数组的大小时,如何获取JSON数组中的最后一个索引?

时间:2016-02-27 00:35:56

标签: javascript json node.js for-loop

我有一组JSON对象。这个数组在for循环中,并且随着信息从数据库中获得,它会不断地将它们添加到数组中。数组中对象的数量可能与从数据库返回的结果数量不同(因为只有一些位置可能具有用户正在搜索的食物类型)。

例如,我可以在rows返回12家餐馆,但只有3家卖汉堡包,所以我不能只做if (rows.length - 1 == i),因为i只会去当rows.length - 1为11时,达到2。

因此匹配返回结果(JSON)在for循环中逐个添加。我永远不会先发制人地知道有多少餐馆出售汉堡,直到所有这些餐馆都添加到阵列中。

我尝试了各种技巧,而我从节点得到的常见错误是"不能多次发送标题。"而且我知道为什么它会给我这个错误。它给了我这个错误,因为循环的每次迭代都会返回它在数组中的任何内容。

输出的一个例子

第一次迭代:

{ "results": [ {"name_of_restaurant": "joes burgers", "open_now": true }] }

第二次迭代:

{ "results": [ {"name_of_restaurant": "joes burgers", "open_now": true }, { "name_of_restaurant": "five guys", "open_now": true }] }

第三次迭代:

{ "results": [ { "name_of_restaurant": "joes burgers", "open_now": true }, "{ name_of_restaurant": "five guys", "open_now": true }, " { name_of_restaurant": "shake shack", "open_now": true }] }

我想要一种捕获第三次迭代的方法,以便将其发送回客户端。

要明确,我不是在寻找array.length - 1。我的问题要复杂得多。

编辑 - 已添加代码

function retrieveLocation(callback) {
    var locationsWithinVisibleMapRectQuery = "SELECT * FROM locations WHERE Y(coordinates) > " + req.body.SWCoordLat + "AND Y(coordinates) < " + req.body.NECoordLat + "AND X(coordinates) > " + req.body.SWCoordLong + "AND X(coordinates) < " + req.body.NECoordLong + ";";
    connection.query(locationsWithinVisibleMapRectQuery, function(err, rows) {
        if (err) throw err; 

        var jsonObject = {
            "results" : []
        };

        //console.log("Number of businesses: " + rows.length);

        for(var i = 0; i < rows.length; i++) {

            console.log("Business number " + i); 
            var businessName = rows[i].name;
            console.log(businessName);
            console.log();
            var x = rows[i].coordinates.x; 
            var y = rows[i].coordinates.y; 

            getMenuForEachLocation(x, y, businessName, rows, i, function(err, obj) {

                if (err) {
                    callback(err, null); 
                }  

                jsonObject["results"].push(obj);

                if( jsonObject["results"] == the last index) { // figure a way to get last iteration to send back as a response
                     callback(null, jsonObject);  
                 }
            }); 
        }
    }); 
}

retrieveLocation(function(err, jsonObject) {
    if (err) throw err; 

    res.json(jsonObject);
});

3 个答案:

答案 0 :(得分:2)

检查.length results数组的.length等于rows数组results的方法的工作示例。请注意,由于i是异步填充的,因此生成的数组可能不是var rows = [0, 1, 2, 3, 4, 5, 6] , results = [] , asyncFn = function(n) { return new Promise(function(resolve) { setTimeout(function() { resolve(n) }, Math.random() * 3000) }) } , complete = function(callback) { for (var i = 0; i < rows.length; i++) { asyncFn(i).then(function(data) { results.push(data); console.log(results); if (results.length === rows.length) callback(rows, results) }) } } complete( // `callback` : do stuff when `results` `.length` is equal to `rows` `.length` function(rows_, results_) { console.log(rows_, results_) alert("complete"); } );的顺序

${testBean.test["hello"]}

答案 1 :(得分:1)

据我所知,getCheckinsForEachLocation()的回调函数仅在条件满足时触发,因此无法知道何时在回调函数内处理所有数据。

我们目前知道rows.length有多少行,而我们需要知道的是当所有getCheckinsForEachLocation()被触发时,我们需要另一个索引和oncomplete回调。

这是一个有效的例子:

var globalIndex;

// Pseudo async function
function getCheckinsForEachLocation (rows, i, callback, oncomplete) {
  setTimeout(function () {
    if (-1 != rows[i].indexOf('burgers')) {
      callback(null, rows[i]);
    }

    // Add up the times that the function was called to
    // find out if they have called all.
    if (++globalIndex == rows.length) {
      oncomplete();
    }
  }, Math.random() * 3000);
}

function retrieveLocation(callback) {
  // Pseudo data retrived from database
  var rows = ["sandwich", "burgers 1", "salad", "burgers 2", "sushi", "burgers 3", "tea"];

  var jsonObject = {
    "results" : []
  };

  // Reset the time that `getCheckinsForEachLocation` was called
  globalIndex = 0;

  for (var i = 0, rowsLength = rows.length; i < rowsLength; ++i) {
    console.log("Business number " + i);
    getCheckinsForEachLocation(rows, i, function(err, obj) {
      if (err) {
        callback(err, null);
      }

      jsonObject["results"].push(obj);
    }, function () {
      callback(null, jsonObject);
    });
  }
}

retrieveLocation(function(err, jsonObject) {
    if (err) throw err;

    alert(JSON.stringify(jsonObject));
});

答案 2 :(得分:1)

我之前提到了承诺 - 这可能适合您的需求。我绝对建议您查看有关承诺的更多信息。快速注意,这都是es6 - 所以不要过于束缚w / arrow函数语法等。如果你正在运行node 4.0&gt; =那么这应该是开箱即用的;

function retrieveLocation() {
    const locationsWithinVisibleMapRectQuery = "SELECT * FROM locations WHERE Y(coordinates) > " + req.body.SWCoordLat + "AND Y(coordinates) < " + req.body.NECoordLat + "AND X(coordinates) > " + req.body.SWCoordLong + "AND X(coordinates) < " + req.body.NECoordLong + ";";
    return new Promise((resolve, reject) => {
      connection.query(locationsWithinVisibleMapRectQuery, (err, rows) => {
        if (err) reject(err);
        resolve(rows);
      });
    })
    .then(rows => {
      return Promise.all(rows.map(row => {
        const businessName = row.name;
        const x = row.coordinates.x; 
        const y = row.coordinates.y; 
        return new Promise((resolve, reject) => {
          getCheckinsForEachLocation(x, y, businessName, rows, i, (err, result) => {
            if (err) reject (err);
            resolve(result);
          })
        })
        .then(result => result)
        .catch(err => {
          throw new Error(err)
        }); 
      }));
    })
    .then(result => result[result.length - 1]); 
}

retrieveLocation()
  .then(jsonObject => res.json(jsonObject))
  .catch(err => console.log(err));