How do I get the final results from my forEach loop?

2154
8
Jump to solution
05-10-2021 11:54 AM
dcarson1661
New Contributor III

Hi All,

I could use some help (be kind I'm extremely new to JavaScript).

I'm trying to get an array of unique values (MICROFILMS) from all layers/selected features in my map.

My problem is the end result in the program below ends up being an empty array, but logging the array inside the forEach I see my results being built. How do I get the final array after it loops through each layer/feature?

What I think is happening is that my function has not finished adding the values to my array before it moves on. I'm assuming I need to add some sort wait, but I really can't seem to get the logic/syntax down. 

 

<html>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
    <title>Intro to MapView - Create a 2D map | Sample | ArcGIS API for JavaScript 4.19</title>
    <style>
      html,
      body,
      #viewDiv {
        padding: 0;
        margin: 0;
        height: 100%;
        width: 100%;
      }
      #layerToggle {
        top: 20px;
        right: 20px;
        position: absolute;
        z-index: 99;
        background-color: white;
        border-radius: 8px;
        padding: 10px;
        opacity: 0.75;
      }
    </style>

    <link rel="stylesheet" href="https://js.arcgis.com/4.19/esri/themes/light/main.css" />
    <script src="https://js.arcgis.com/4.19/"></script>

    <script>
      require([
          "esri/Map", 
          "esri/views/MapView",
          "esri/layers/FeatureLayer",
          "esri/widgets/LayerList",
          "esri/widgets/Sketch/SketchViewModel",
          "esri/layers/GraphicsLayer",
          "esri/geometry/geometryEngineAsync"
        ], (
            Map, 
            MapView,
            FeatureLayer,
            LayerList,
            SketchViewModel,
            GraphicsLayer,
            geometryEngineAsync
            ) => {
        const map = new Map({
          basemap: "topo-vector"
        });

        const view = new MapView({
          container: "viewDiv",
          map: map,
          zoom: 12,
          center: [-122.228558,47.303845]
        });


//Add Layer
        let stormPipes = new FeatureLayer({
          url: "https://services9.arcgis.com/jyf59MjuiWfY46oy/arcgis/rest/services/Storm_Pipes/FeatureServer",
          id: "Storm Pipes",
          title: "Storm Pipes"
        });
        map.add(stormPipes);
//Add Layer
let stormCatchBasins = new FeatureLayer({
          url: "https://services9.arcgis.com/jyf59MjuiWfY46oy/arcgis/rest/services/Storm_Catch_Basins/FeatureServer",
          id: "Storm Catch Basins",
          title: "Storm Catch Basins"
        });
        map.add(stormCatchBasins);


let microFilms = [];

//layerList with Legend //
const layerList = new LayerList({
  view: view,
  listItemCreatedFunction: function(event) {
    const item = event.item;
    if (item.layer.type != "group") {
      item.panel = {
        content: "legend",
        open: false
      };
    }
  }
});
view.ui.add(layerList, "top-right");
//

//polgon sketch tool//////////////////////////
polygonGraphicsLayer = new GraphicsLayer({listMode: "hide"});
map.add(polygonGraphicsLayer);
view.ui.add("select-by-polygon", "top-left");
const selectButton = document.getElementById("select-by-polygon");
selectButton.addEventListener("click", function() {
  polygonGraphicsLayer.removeAll();
  microFilms = []
  view.popup.close();
  sketchViewModel.create("rectangle");


});

sketchViewModel = new SketchViewModel({
  view: view,
  layer: polygonGraphicsLayer,
  pointSymbol: {
    type: "simple-fill", 
    color: "yellow",
    style: "solid",
    outline: {
    color: "red",
    width: 1
    }
  }

});

function onlyUnique(value, index, self) {
  return self.indexOf(value) === index;
}

sketchViewModel.on("create", async (event) => {
if (event.state === "complete") {
    
view.map.layers.map(function(lyr){
    if (lyr.visible === true && lyr.type != "graphics"){
    console.log(lyr.title);
}
});

const geometries = polygonGraphicsLayer.graphics.map(function(graphic){
    return graphic.geometry
});
const queryGeometry = await geometryEngineAsync.union(geometries.toArray());

function getMicroFilms (){
    view.map.layers.forEach((layer) => {if (layer.visible === true && layer.type != "graphics"){
    const query = {
    geometry: queryGeometry,
    outFields: ["*"]
};

    layer.queryFeatures(query).then((results) => {
        results.features.forEach((feature) => {if (feature.attributes.MICROFILM != "UNKNOWN"){
            microFilms.push(feature.attributes.MICROFILM)}});
            
            console.log(microFilms.filter(onlyUnique))
    
    })
return microFilms
}});
}

console.log(getMicroFilms()); //NEED HELP HERE (this returns an empty array)

   
   

}
});




      });
    </script>
  </head>

  <body>
    <div id="viewDiv"></div>
    <div
    id="select-by-polygon"
    class="esri-widget esri-widget--button esri-widget esri-interactive"
    title="Select features by polygon"
  >    <span class="esri-icon-checkbox-unchecked"></span>
  </body>
</html>

 

 

0 Kudos
1 Solution

Accepted Solutions
JeffreyWilkerson
Occasional Contributor III

Yeah, sorry, you can't just leave 'All' hanging like that, it's actually another promise so it needs a function for its asynchronous result.  I've updated your code below to not error out.  I'm not sure how you are trying to access the sketch mode, but at least it won't error out when you run it.  

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
    <title>Intro to MapView - Create a 2D map | Sample | ArcGIS API for JavaScript 4.19</title>
    <style>
        html,
        body,
        #viewDiv {
            padding: 0;
            margin: 0;
            height: 100%;
            width: 100%;
        }

        #layerToggle {
            top: 20px;
            right: 20px;
            position: absolute;
            z-index: 99;
            background-color: white;
            border-radius: 8px;
            padding: 10px;
            opacity: 0.75;
        }
    </style>

    <link rel="stylesheet" href="https://js.arcgis.com/4.19/esri/themes/light/main.css" />
    <script src="https://js.arcgis.com/4.19/"></script>

    <script>
        require([
            "esri/Map",
            "esri/views/MapView",
            "esri/layers/FeatureLayer",
            "esri/widgets/LayerList",
            "esri/widgets/Sketch/SketchViewModel",
            "esri/layers/GraphicsLayer",
            "esri/geometry/geometryEngineAsync",
            "esri/layers/GroupLayer",
            "dojo/Deferred",
            "dojo/promise/all"
        ], (
            Map,
            MapView,
            FeatureLayer,
            LayerList,
            SketchViewModel,
            GraphicsLayer,
            geometryEngineAsync,
            GroupLayer,
            Deferred,
            all
        ) => {
            const map = new Map({
                basemap: "topo-vector"
            });

            const view = new MapView({
                container: "viewDiv",
                map: map,
                zoom: 12,
                center: [-122.228558, 47.303845]
            });


            //Add Layer
            let stormPipes = new FeatureLayer({
                url: "https://services9.arcgis.com/jyf59MjuiWfY46oy/arcgis/rest/services/Storm_Pipes/FeatureServer",
                id: "Storm Pipes",
                title: "Storm Pipes"
            });
            map.add(stormPipes);
            //Add Layer
            let stormCatchBasins = new FeatureLayer({
                url: "https://services9.arcgis.com/jyf59MjuiWfY46oy/arcgis/rest/services/Storm_Catch_Basins/FeatureServer",
                id: "Storm Catch Basins",
                title: "Storm Catch Basins"
            });
            map.add(stormCatchBasins);


            let microFilms = [];

            //layerList with Legend //
            const layerList = new LayerList({
                view: view,
                listItemCreatedFunction: function (event) {
                    const item = event.item;
                    if (item.layer.type != "group") {
                        item.panel = {
                            content: "legend",
                            open: false
                        };
                    }
                }
            });
            view.ui.add(layerList, "top-right");
            //

            /* var stormGroup = new GroupLayer({
                    title: "Storm",
                    layers: [stormPipes, stormCatchBasins]
                  });

                  map.add(stormGroup);
            console.log(stormGroup)
            */


            //polgon sketch tool//////////////////////////
            polygonGraphicsLayer = new GraphicsLayer({ listMode: "hide" });
            map.add(polygonGraphicsLayer);
            view.ui.add("select-by-polygon", "top-left");
            const selectButton = document.getElementById("select-by-polygon");
            selectButton.addEventListener("click", function () {
                polygonGraphicsLayer.removeAll();
                microFilms = []
                view.popup.close();
                sketchViewModel.create("rectangle");


            });

            sketchViewModel = new SketchViewModel({
                view: view,
                layer: polygonGraphicsLayer,
                pointSymbol: {
                    type: "simple-fill",
                    color: "yellow",
                    style: "solid",
                    outline: {
                        color: "red",
                        width: 1
                    }
                }

            });

            function onlyUnique(value, index, self) {
                return self.indexOf(value) === index;
            }

            sketchViewModel.on("create", async (event) => {
                if (event.state === "complete") {

                    view.map.layers.map(function (lyr) {
                        if (lyr.visible === true && lyr.type != "graphics" && lyr.type != "group") {
                            console.log(lyr.title);
                        }
                    });

                    const geometries = polygonGraphicsLayer.graphics.map(function (graphic) {
                        return graphic.geometry
                    });
                    const queryGeometry = await geometryEngineAsync.union(geometries.toArray());

                    let promises = [];
                    //let microFilms = [];
                    function getMicroFilms() {
                        view.map.layers.forEach((layer) => {
                            if (layer.visible === true && layer.type != "graphics") {
                                const query = {
                                    geometry: queryGeometry,
                                    outFields: ["*"]
                                };
                                let defLayer = new Deferred;
                                promises.push(defLayer);
                                layer.queryFeatures(query)
                                    .then((results) => {
                                        results.features.forEach((feature) => {
                                            if (feature.attributes.MICROFILM != "UNKNOWN") {
                                                microFilms.push(feature.attributes.MICROFILM);
                                            }
                                        });
                                        return defLayer.resolve(results);
                                    })
                                    .catch(function (error) {
                                        console.error(outError);
                                        return defAmenities.reject(error);
                                    });
                            }
                        });

                        all(promises).then(function (results) {
                            console.log("After all promises are returned");
                            console.log(results);
                            console.log(microFilms); //.filter(onlyUnique));
                            return microFilms;
                        });
                    }

                    console.log(microFilms); //getMicroFilms());

                }
            });




        });
    </script>
</head>

<body>
    <div id="viewDiv"></div>
    <div id="select-by-polygon"
         class="esri-widget esri-widget--button esri-widget esri-interactive"
         title="Select features by polygon">    <span class="esri-icon-checkbox-unchecked"></span>
</body>
</html>

 

View solution in original post

8 Replies
BlakeTerhune
MVP Regular Contributor

You could probably benefit from cleaning up your indentation to keep track of your scopes. It looks like the return statement for getMicroFilms() is inside the forEach() loop; meaning it will kick out after checking the first feature in the FeatureSet. This is probably what you want the function to look like.

function getMicroFilms () {
    view.map.layers.forEach((layer) => {
        if (layer.visible === true && layer.type != "graphics") {
            const query = {
                geometry: queryGeometry,
                outFields: ["*"]
            };
            layer.queryFeatures(query).then((results) => {
                results.features.forEach((feature) => {
                    if (feature.attributes.MICROFILM != "UNKNOWN") {
                        microFilms.push(feature.attributes.MICROFILM);
                    }
                });
                console.log(microFilms.filter(onlyUnique));
            });
        }
    });
    return microFilms;
}

 

 

0 Kudos
dcarson1661
New Contributor III

Thanks for the response! That does make things a bit more clear, but I'm still getting an empty array.

0 Kudos
BlakeTerhune
MVP Regular Contributor

Try logging the query results before you start looping so you can verify if there are actually any features where MICROFILM != "UNKNOWN"

0 Kudos
JeffreyWilkerson
Occasional Contributor III

The layers forEach will cycle through each layer synchronously, but the call to 'queryFeatures' results in an asynchronous promise.  Esri has a PromiseAll option that I haven't been able to figure out yet, but I have good results using the Dojo versions.  

If you add "dojo/Deferred", "dojo/promise/all" to the 'require' statement (and the associated variables), you might try something like this:

let promises = [];
let microFilms = [];
function getMicroFilms () {
    view.map.layers.forEach((layer) => {
        if (layer.visible === true && layer.type != "graphics") {
            const query = {
                geometry: queryGeometry,
                outFields: ["*"]
            };
            let defLayer = new Deferred;
            promises.push(defLayer);
            layer.queryFeatures(query)
                .then((results) => {
                    results.features.forEach((feature) => {
                        if (feature.attributes.MICROFILM != "UNKNOWN") {
                            microFilms.push(feature.attributes.MICROFILM);
                        }
                    });
                    return defLayer.resolve(results);
                })
                .catch(function (error) {
                    console.error(outError);
                    return defAmenities.reject(error);
                });
        }
    });

    all(promises) {
        console.log(microFilms.filter(onlyUnique));
        return microFilms;
    }
}

That may work.  If not, separate the queryFeatures calls into another function and create the deferred there.  Either way the All will only run after all of the separate deferreds have run successfully or failed.

 

 

dcarson1661
New Contributor III

Thank you, I think this might work but I could use some help implementing. I've updated the requirements and function but am getting an error at line: all(promises) {

Error: ';' expected

<html>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
    <title>Intro to MapView - Create a 2D map | Sample | ArcGIS API for JavaScript 4.19</title>
    <style>
      html,
      body,
      #viewDiv {
        padding: 0;
        margin: 0;
        height: 100%;
        width: 100%;
      }
      #layerToggle {
        top: 20px;
        right: 20px;
        position: absolute;
        z-index: 99;
        background-color: white;
        border-radius: 8px;
        padding: 10px;
        opacity: 0.75;
      }
    </style>

    <link rel="stylesheet" href="https://js.arcgis.com/4.19/esri/themes/light/main.css" />
    <script src="https://js.arcgis.com/4.19/"></script>

    <script>
      require([
          "esri/Map", 
          "esri/views/MapView",
          "esri/layers/FeatureLayer",
          "esri/widgets/LayerList",
          "esri/widgets/Sketch/SketchViewModel",
          "esri/layers/GraphicsLayer",
          "esri/geometry/geometryEngineAsync",
          "esri/layers/GroupLayer",
          "dojo/Deferred",
          "dojo/promise/all"
        ], (
            Map, 
            MapView,
            FeatureLayer,
            LayerList,
            SketchViewModel,
            GraphicsLayer,
            geometryEngineAsync,
            GroupLayer,
            Deferred,
            all
            ) => {
        const map = new Map({
          basemap: "topo-vector"
        });

        const view = new MapView({
          container: "viewDiv",
          map: map,
          zoom: 12,
          center: [-122.228558,47.303845]
        });


//Add Layer
        let stormPipes = new FeatureLayer({
          url: "https://services9.arcgis.com/jyf59MjuiWfY46oy/arcgis/rest/services/Storm_Pipes/FeatureServer",
          id: "Storm Pipes",
          title: "Storm Pipes"
        });
        map.add(stormPipes);
//Add Layer
let stormCatchBasins = new FeatureLayer({
          url: "https://services9.arcgis.com/jyf59MjuiWfY46oy/arcgis/rest/services/Storm_Catch_Basins/FeatureServer",
          id: "Storm Catch Basins",
          title: "Storm Catch Basins"
        });
        map.add(stormCatchBasins);


let microFilms = [];

//layerList with Legend //
const layerList = new LayerList({
  view: view,
  listItemCreatedFunction: function(event) {
    const item = event.item;
    if (item.layer.type != "group") {
      item.panel = {
        content: "legend",
        open: false
      };
    }
  }
});
view.ui.add(layerList, "top-right");
//

/* var stormGroup = new GroupLayer({
        title: "Storm",
        layers: [stormPipes, stormCatchBasins]
      });

      map.add(stormGroup); 
console.log(stormGroup)
*/


//polgon sketch tool//////////////////////////
polygonGraphicsLayer = new GraphicsLayer({listMode: "hide"});
map.add(polygonGraphicsLayer);
view.ui.add("select-by-polygon", "top-left");
const selectButton = document.getElementById("select-by-polygon");
selectButton.addEventListener("click", function() {
  polygonGraphicsLayer.removeAll();
  microFilms = []
  view.popup.close();
  sketchViewModel.create("rectangle");


});

sketchViewModel = new SketchViewModel({
  view: view,
  layer: polygonGraphicsLayer,
  pointSymbol: {
    type: "simple-fill", 
    color: "yellow",
    style: "solid",
    outline: {
    color: "red",
    width: 1
    }
  }

});

function onlyUnique(value, index, self) {
  return self.indexOf(value) === index;
}

sketchViewModel.on("create", async (event) => {
if (event.state === "complete") {
    
view.map.layers.map(function(lyr){
    if (lyr.visible === true && lyr.type != "graphics" && lyr.type != "group"){
    console.log(lyr.title);
}
});

const geometries = polygonGraphicsLayer.graphics.map(function(graphic){
    return graphic.geometry
});
const queryGeometry = await geometryEngineAsync.union(geometries.toArray());

let promises = [];
let microFilms = [];
function getMicroFilms () {
    view.map.layers.forEach((layer) => {
        if (layer.visible === true && layer.type != "graphics") {
            const query = {
                geometry: queryGeometry,
                outFields: ["*"]
            };
            let defLayer = new Deferred;
            promises.push(defLayer);
            layer.queryFeatures(query)
                .then((results) => {
                    results.features.forEach((feature) => {
                        if (feature.attributes.MICROFILM != "UNKNOWN") {
                            microFilms.push(feature.attributes.MICROFILM);
                        }
                    });
                    return defLayer.resolve(results);
                })
                .catch(function (error) {
                    console.error(outError);
                    return defAmenities.reject(error);
                });
        }
    });

    all(promises) {
        console.log(microFilms.filter(onlyUnique));
        return microFilms;
    }
}

console.log(getMicroFilms());
    
}});




      });
    </script>
  </head>

  <body>
    <div id="viewDiv"></div>
    <div
    id="select-by-polygon"
    class="esri-widget esri-widget--button esri-widget esri-interactive"
    title="Select features by polygon"
  >    <span class="esri-icon-checkbox-unchecked"></span>
  </body>
</html>

 

0 Kudos
JeffreyWilkerson
Occasional Contributor III

Yeah, sorry, you can't just leave 'All' hanging like that, it's actually another promise so it needs a function for its asynchronous result.  I've updated your code below to not error out.  I'm not sure how you are trying to access the sketch mode, but at least it won't error out when you run it.  

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
    <title>Intro to MapView - Create a 2D map | Sample | ArcGIS API for JavaScript 4.19</title>
    <style>
        html,
        body,
        #viewDiv {
            padding: 0;
            margin: 0;
            height: 100%;
            width: 100%;
        }

        #layerToggle {
            top: 20px;
            right: 20px;
            position: absolute;
            z-index: 99;
            background-color: white;
            border-radius: 8px;
            padding: 10px;
            opacity: 0.75;
        }
    </style>

    <link rel="stylesheet" href="https://js.arcgis.com/4.19/esri/themes/light/main.css" />
    <script src="https://js.arcgis.com/4.19/"></script>

    <script>
        require([
            "esri/Map",
            "esri/views/MapView",
            "esri/layers/FeatureLayer",
            "esri/widgets/LayerList",
            "esri/widgets/Sketch/SketchViewModel",
            "esri/layers/GraphicsLayer",
            "esri/geometry/geometryEngineAsync",
            "esri/layers/GroupLayer",
            "dojo/Deferred",
            "dojo/promise/all"
        ], (
            Map,
            MapView,
            FeatureLayer,
            LayerList,
            SketchViewModel,
            GraphicsLayer,
            geometryEngineAsync,
            GroupLayer,
            Deferred,
            all
        ) => {
            const map = new Map({
                basemap: "topo-vector"
            });

            const view = new MapView({
                container: "viewDiv",
                map: map,
                zoom: 12,
                center: [-122.228558, 47.303845]
            });


            //Add Layer
            let stormPipes = new FeatureLayer({
                url: "https://services9.arcgis.com/jyf59MjuiWfY46oy/arcgis/rest/services/Storm_Pipes/FeatureServer",
                id: "Storm Pipes",
                title: "Storm Pipes"
            });
            map.add(stormPipes);
            //Add Layer
            let stormCatchBasins = new FeatureLayer({
                url: "https://services9.arcgis.com/jyf59MjuiWfY46oy/arcgis/rest/services/Storm_Catch_Basins/FeatureServer",
                id: "Storm Catch Basins",
                title: "Storm Catch Basins"
            });
            map.add(stormCatchBasins);


            let microFilms = [];

            //layerList with Legend //
            const layerList = new LayerList({
                view: view,
                listItemCreatedFunction: function (event) {
                    const item = event.item;
                    if (item.layer.type != "group") {
                        item.panel = {
                            content: "legend",
                            open: false
                        };
                    }
                }
            });
            view.ui.add(layerList, "top-right");
            //

            /* var stormGroup = new GroupLayer({
                    title: "Storm",
                    layers: [stormPipes, stormCatchBasins]
                  });

                  map.add(stormGroup);
            console.log(stormGroup)
            */


            //polgon sketch tool//////////////////////////
            polygonGraphicsLayer = new GraphicsLayer({ listMode: "hide" });
            map.add(polygonGraphicsLayer);
            view.ui.add("select-by-polygon", "top-left");
            const selectButton = document.getElementById("select-by-polygon");
            selectButton.addEventListener("click", function () {
                polygonGraphicsLayer.removeAll();
                microFilms = []
                view.popup.close();
                sketchViewModel.create("rectangle");


            });

            sketchViewModel = new SketchViewModel({
                view: view,
                layer: polygonGraphicsLayer,
                pointSymbol: {
                    type: "simple-fill",
                    color: "yellow",
                    style: "solid",
                    outline: {
                        color: "red",
                        width: 1
                    }
                }

            });

            function onlyUnique(value, index, self) {
                return self.indexOf(value) === index;
            }

            sketchViewModel.on("create", async (event) => {
                if (event.state === "complete") {

                    view.map.layers.map(function (lyr) {
                        if (lyr.visible === true && lyr.type != "graphics" && lyr.type != "group") {
                            console.log(lyr.title);
                        }
                    });

                    const geometries = polygonGraphicsLayer.graphics.map(function (graphic) {
                        return graphic.geometry
                    });
                    const queryGeometry = await geometryEngineAsync.union(geometries.toArray());

                    let promises = [];
                    //let microFilms = [];
                    function getMicroFilms() {
                        view.map.layers.forEach((layer) => {
                            if (layer.visible === true && layer.type != "graphics") {
                                const query = {
                                    geometry: queryGeometry,
                                    outFields: ["*"]
                                };
                                let defLayer = new Deferred;
                                promises.push(defLayer);
                                layer.queryFeatures(query)
                                    .then((results) => {
                                        results.features.forEach((feature) => {
                                            if (feature.attributes.MICROFILM != "UNKNOWN") {
                                                microFilms.push(feature.attributes.MICROFILM);
                                            }
                                        });
                                        return defLayer.resolve(results);
                                    })
                                    .catch(function (error) {
                                        console.error(outError);
                                        return defAmenities.reject(error);
                                    });
                            }
                        });

                        all(promises).then(function (results) {
                            console.log("After all promises are returned");
                            console.log(results);
                            console.log(microFilms); //.filter(onlyUnique));
                            return microFilms;
                        });
                    }

                    console.log(microFilms); //getMicroFilms());

                }
            });




        });
    </script>
</head>

<body>
    <div id="viewDiv"></div>
    <div id="select-by-polygon"
         class="esri-widget esri-widget--button esri-widget esri-interactive"
         title="Select features by polygon">    <span class="esri-icon-checkbox-unchecked"></span>
</body>
</html>

 

dcarson1661
New Contributor III

You're awesome, this is exactly what I needed! Thank you!

0 Kudos
JeffreyWilkerson
Occasional Contributor III

Glad you figured it out.  Just one last thing, the 'catch' for the layer.queryFeatures should be:

 

.catch(function (error) {
    console.error(outError);
    return defLayer.reject(error);
 });

 

 

 

0 Kudos