23

I'm trying to submit a query using the postal code to my DB whenever the googlemaps viewport center changes. I know that this can be done with reverse geocoding with something like:

google.maps.event.addListener(map, 'center_changed', function(){
newCenter();
});
...
function newCenter(){
var newc = map.getCenter();
geocoder.geocode({'latLng': newc}, function(results, status){
if (status == google.maps.GeocoderStatus.OK) {
  var newzip = results[0].address_components['postal_code'];
  }
});
};

Of course, this code doesn't actually work. So I was wondering how I would need to change this in order to extract the postal code from the results array. Thanks

Romaine M.
  • 371
  • 1
  • 3
  • 10
  • EDIT: It looks like I have no choice but to iterate through the address components and search for one with types[0] == 'postal_code' ? Is there a better way to do that? – Romaine M. Mar 17 '11 at 15:28
  • @RomainM yes i would assume unless u want to manually parse it from the result – KJYe.Name Mar 17 '11 at 15:31
  • Yes, you do have to go through the array and look for the element that has 'postal_code' in its `types` array, which may be last or first or anywhere in between. Besides, which element? You may want to take the postal code from `results[0].address_components` or from `results` itself: try with both and see what works best in the area you care about. In general, I'd recommend `results[0].address_components` if you care about places with a full address, and `results` if you case about strict containment of your latlng in the postal code you get. – miguev Jul 08 '16 at 15:14

23 Answers23

23

What I've realized so far is that in most cases the ZIPCODE is always the last value inside each returned address, so, if you want to retrieve the very first zipcode (this is my case), you can use the following approach:

var address = results[0].address_components;
var zipcode = address[address.length - 1].long_name;
Tuco
  • 1,620
  • 1
  • 14
  • 15
  • ah, there we go. That's a hell of a lot cleaner. Thanks – Romaine M. Mar 18 '11 at 15:51
  • 1
    I had to use `address.length - 2` Not sure why yours is different? – Andy Lobel Jul 08 '14 at 11:52
  • 2
    There's a long time since I published this answer, but I remember that in another project I discovered that the index would vary depending on the result. In other words, the zipcode can sometimes not be the last item. – Tuco Jul 10 '14 at 01:17
  • 2
    @Tuco, the index does change. Sometimes it is the last element, others, it is the second to last. Not sure the reason for this though. – jmiraglia Jan 05 '15 at 15:26
  • Indeed @jmiraglia. Long after I published this response, I discovered that for some reason sometimes it's not the last element. Maybe you can validate the value against a Regex, something like /\d{5}-\d{3}/ could to the trick. – Tuco Jan 06 '15 at 17:25
  • 1
    Yes, you do have to go through the array and look for the element that has 'postal_code' in its `types` array, which may be last or first or anywhere in between. – miguev Jul 08 '16 at 15:14
  • 2
    For those who are wondering why zip code comes at the second last position sometimes, it's because in some cases the last item is the `postal_code_suffix` key – asprin Nov 16 '16 at 05:02
  • it can also miss if you do not provide enough information, and can lead to error – Thanatos11th May 22 '17 at 18:46
  • I was testing out and for few cases `address[address.length - 1]` returns the desired zip code while in few cases `address[address.length - 2]. So here is a ternary statement ```var zipcode = isNaN(address[address.length - 1].long_name) ? address[address.length - 2].long_name : address[address.length - 1].long_name;``` – Shubhamoy Nov 27 '18 at 08:13
14

You can do this pretty easily using the underscore.js libraray: http://documentcloud.github.com/underscore/#find

_.find(results[0].address_components, function (ac) { return ac.types[0] == 'postal_code' }).short_name
Jeff S
  • 554
  • 5
  • 13
  • thanks buddy, pretty nice one-liner using underscore.js' find() – jpincheira Apr 17 '12 at 10:50
  • The underscore hack is very good. i'm not using underscore in my current project and i don't feel like adding another lib just for it, but i'm bookmarking this answer :D – guinetik Jan 15 '15 at 00:37
  • See this answer https://stackoverflow.com/a/62945561/2264626 with native `.find` method – Rahil Wazir Jul 17 '20 at 00:59
12

Alright, so I got it. The solution is a little uglier than I'd like, and I probably don't need the last for loop, but here's the code for anyone else who needs to extract crap from address_components[]. This is inside the geocoder callback function

// make sure to initialize i
for(i=0; i < results.length; i++){
            for(var j=0;j < results[i].address_components.length; j++){
                for(var k=0; k < results[i].address_components[j].types.length; k++){
                    if(results[i].address_components[j].types[k] == "postal_code"){
                        zipcode = results[i].address_components[j].short_name;
                    }
                }
            }
    }
Swisher Sweet
  • 769
  • 11
  • 33
Romaine M.
  • 371
  • 1
  • 3
  • 10
  • this doesn't work for me, I am checking the postal code for Iowa City Iowa and it comes up undefined. – Sam Cromer Oct 09 '14 at 15:18
  • This should be the approved answer. This works for addresses that do not have the zip code as the last component. – jmiraglia Jan 05 '15 at 15:30
  • 2
    hey @SamCromer i tested the above code and the answer also came undefined. maybe you also forgot to put var i= 0 before the for loop. i added and it worked great! – guinetik Jan 15 '15 at 00:38
  • 2
    this works beautifully, just as long as you change 'for(i; i < results.length; i++){' to 'for(i=0; i < results.length; i++){'. many thanks for this solution. – luke_mclachlan Feb 02 '15 at 17:10
12

Using JQuery?

var searchAddressComponents = results[0].address_components,
    searchPostalCode="";

$.each(searchAddressComponents, function(){
    if(this.types[0]=="postal_code"){
        searchPostalCode=this.short_name;
    }
});

short_name or long_name will work above
the "searchPostalCode" var will contain the postal (zip?) code IF and only IF you get one from the Google Maps API.

Sometimes you DO NOT get a "postal_code" in return for your query.

zephyr
  • 418
  • 5
  • 9
EMuentes
  • 562
  • 1
  • 8
  • 18
4
$.each(results[0].address_components,function(index,value){
    if(value.types[0] === "postal_code"){
        $('#postal_code').val(value.long_name);
    }
});
echo_mongi
  • 41
  • 2
  • 7
3

You can also use JavaScript .find method which is similar to underscore _.find method but it is native and require no extra dependency.

const zip_code = results[0].address_components.find(addr => addr.types[0] === "postal_code").short_name;
Rahil Wazir
  • 10,007
  • 11
  • 42
  • 64
  • You need a Polyfill for this, cause there is no IE support [https://caniuse.com/array-find] – MadMax Oct 16 '20 at 11:12
2

You can also use this code, this function will help to get zip on button click or onblur or keyup or keydown.

Just pass the address to this function.

use google api with valid key and sensor option removed as it doesn't required now.

function callZipAPI(addSearchZip)
{    
    var geocoder = new google.maps.Geocoder();
    var zipCode = null;

    geocoder.geocode({ 'address': addSearchZip }, function (results, status) {
        if (status == google.maps.GeocoderStatus.OK) {

            //var latitude = results[0].geometry.location.lat();
            //var longitude = results[0].geometry.location.lng();

            var addressComponent = results[0].address_components;            
            for (var x = 0 ; x < addressComponent.length; x++) {
                var chk = addressComponent[x];
                if (chk.types[0] == 'postal_code') {
                    zipCode = chk.long_name;
                }
            }
            if (zipCode) {
                alert(zipCode);
            }
            else {
                alert('No result found!!');
            }
        } else {            
            alert('Enter proper address!!');
        }
    });
}
Frits
  • 7,341
  • 10
  • 42
  • 60
2

I use this code to get "Postal code" and "locality", but you can use it to get any other field just changing the value of type:

JAVASCRIPT

var address = results[0].address_components;
var zipcode = '';
var locality = '';

for (var i = 0; i < address.length; i++) {
     if (address[i].types.includes("postal_code")){ zipcode = address[i].short_name; }    
     if (address[i].types.includes("locality")){ locality = address[i].short_name; }
}
santillanix
  • 1,947
  • 18
  • 17
2

I think rather than depending on the index it better checks address type key inside the component. I solved this issue by using a switch case.

      var address = '';
      var pin = '';
      var country = '';
      var state = '';
      var city = '';
      var streetNumber = '';
      var route ='';
      var place = autocomplete.getPlace(); 
      for (var i = 0; i < place.address_components.length; i++) {
        var component = place.address_components[i];
        var addressType = component.types[0];

        switch (addressType) {
            case 'street_number':
                streetNumber = component.long_name;
                break;
            case 'route':
                route = component.short_name;
                break;
            case 'locality':
                city = component.long_name;
                break;
            case 'administrative_area_level_1':
                state = component.long_name;
                break;
            case 'postal_code':
                pin = component.long_name;
                break;
            case 'country':
                country = component.long_name;
                break;
        }
       }
webmatrix
  • 122
  • 1
  • 2
2

This takes only two for loops. The "results" array gets updated once we found the first "type" to be "postal_code".

It then updates the original array with the newly found array set and loops again.

            var i, j,
            result, types;

            // Loop through the Geocoder result set. Note that the results
            // array will change as this loop can self iterate.
            for (i = 0; i < results.length; i++) {

                result = results[i];

                types = result.types;

                for (j = 0; j < types.length; j++) {

                    if (types[j] === 'postal_code') {

                        // If we haven't found the "long_name" property,
                        // then we need to take this object and iterate through
                        // it again by setting it to our master loops array and 
                        // setting the index to -1
                        if (result.long_name === undefined) {
                            results = result.address_components;
                            i = -1;
                        }
                        // We've found it!
                        else {
                            postcode = result.long_name;
                        }

                        break;

                    }

                }

            }
1

Using JSONPath, it's easily done with one line of code:

var zip = $.results[0].address_components[?(@.types=="postal_code")].long_name;
Ray Myers
  • 11
  • 1
1

In PHP I use this code. Almost in every conditions it works.

$zip = $data["results"][3]["address_components"];
$zip = $index[0]["short_name"];
B. Bohdan
  • 480
  • 4
  • 12
1

Romaine M. — thanks! If you just need to find the postal code in the first returned result from Google, you can do just 2 loops:

for(var j=0;j < results[0].address_components.length; j++){
    for(var k=0; k < results[0].address_components[j].types.length; k++){
        if(results[0].address_components[j].types[k] == "postal_code"){
            zipcode = results[0].address_components[j].long_name;
        }
    }
}
1
places.getDetails( request_details, function(results_details, status){

                // Check if the Service is OK
                if (status == google.maps.places.PlacesServiceStatus.OK) {                  

                    places_postal           = results_details.address_components
                    places_phone            = results_details.formatted_phone_number
                    places_phone_int        = results_details.international_phone_number
                    places_format_address   = results_details.formatted_address
                    places_google_url       = results_details.url
                    places_website          = results_details.website
                    places_rating           = results_details.rating

                    for (var i = 0; i < places_postal.length; i++ ) {
                        if (places_postal[i].types == "postal_code"){
                            console.log(places_postal[i].long_name)
                        }
                    }

                }
            });

This seems to work very well for me, this is with the new Google Maps API V3. If this helps anyone, write a comment, i'm writing my script as we speak... so it might change.

user1116928
  • 149
  • 2
0

This simple code works for me

for (var i = 0; i < address.length; i++) {
        alert(address[i].types);
        if (address[i].types == "postal_code")
            $('#postalCode').val(address[i].long_name);
        if (address[i].types == "")
            $('#country').val(address[i].short_name);
    }
0

Using Jquery

  • You cant be sure in which location in the address_components array the postal code is stored. Sometimes in address_components.length - 1 > pincode may not be there. This is true in "Address to latlng" geocoding.
  • You can be sure that Postal code will contain a "postal_code" string. So best way is to check for that.
   var postalObject = $.grep(results[0].address_components, function(n, i) {
                                    if (n.types[0] == "postal_code") {
                                        return n;
                                    } else {
                                        return null;
                                    }
                                });

                                $scope.query.Pincode = postalObject[0].long_name;
Sunny
  • 932
  • 8
  • 22
0
 return $http.get('//maps.googleapis.com/maps/api/geocode/json', {
            params: {
                address: val,
                sensor: false
            }
        }).then(function (response) {
           var model= response.data.results.map(function (item) {
               // return item.address_components[0].short_name;
               var short_name;
             var st=  $.each(item.address_components, function (value, key) {
                    if (key.types[0] == "postal_code") {
                        short_name= key.short_name;
                    }
                });
             return short_name;

            });
                return model;
        });
0
//autocomplete is the text box where u will get the suggestions for an address.

autocomplete.addListener('place_changed', function () {
//Place will get the selected place geocode and returns with the address              
//and marker information.
        var place = autocomplete.getPlace();
//To select just the zip code of complete address from marker, below loop //will help to find. Instead of y.long_name you can also use y.short_name. 
        var zipCode = null;
        for (var x = 0 ; x < place.address_components.length; x++) {
            var y = place.address_components[x];
            if (y.types[0] == 'postal_code') {
                zipCode = y.long_name;
            }
        }
    });
0

It seems that nowadays it's better to get it from the restful API, simply try:

https://maps.googleapis.com/maps/api/geocode/json?latlng=40.714224,-73.961452&key=YOUR_KEY_HERE

Using an AJAX GET call works perfect!

Something like:

var your_api_key = "***";
var f_center_lat = 40.714224;
var f_center_lon = -73.961452;

$.ajax({ url: "https://maps.googleapis.com/maps/api/geocode/json?latlng="+f_center_lat+","+f_center_lon+"&key="+your_api_key,
            method: "GET"
        })
        .done(function( res ) { if (debug) console.log("Ajax result:"); console.log(res);
            var zipCode = null;
            var addressComponent = res.results[0].address_components;
            for (var x = 0 ; x < addressComponent.length; x++) {
                var chk = addressComponent[x];
                if (chk.types[0] == 'postal_code') {
                    zipCode = chk.long_name;
                }
            }
            if (zipCode) {
                //alert(zipCode);
                $(current_map_form + " #postalcode").val(zipCode);
            }
            else {
                //alert('No result found!!');
                if (debug) console.log("Zip/postal code not found for this map location.")
            }
        })
        .fail(function( jqXHR, textStatus ) {
            console.log( "Request failed (get postal code via geocoder rest api). Msg: " + textStatus );
        });
Darío Pm
  • 139
  • 1
  • 9
0

In a word, that's a lot of effort. At least with the v2 API, I could retrieve those details thusly:

var place = response.Placemark[0];
var point = new GLatLng(place.Point.coordinates[1], place.Point.coordinates[0]);

myAddress = place.AddressDetails.Country.AdministrativeArea.SubAdministrativeArea.Locality.Thoroughfare.ThoroughfareName

myCity = place.AddressDetails.Country.AdministrativeArea.SubAdministrativeArea.Locality.LocalityName

myState = place.AddressDetails.Country.AdministrativeArea.AdministrativeAreaName

myZipCode = place.AddressDetails.Country.AdministrativeArea.SubAdministrativeArea.Locality.PostalCode.PostalCodeNumber

There has got to be a more elegant way to retrieve individual address_components without going through the looping jujitsu you just went through.

JFrancis
  • 365
  • 1
  • 10
  • I hope you're right, maybe I just don't get how you're supposed to interact with the object. If you look at the tutorial thing, it's clear that address_components is an array, and since Javascript doesn't have associative arrays, the only way that I can think to do this is with loops http://code.google.com/apis/maps/documentation/javascript/services.html#GeocodingResponses – Romaine M. Mar 17 '11 at 22:43
0

As I got it zip is the last or the one that before last. That why this is my solution

const getZip = function (arr) {
  return (arr[arr.length - 1].types[0] === 'postal_code') ? arr[arr.length - 1].long_name : arr[arr.length - 2].long_name;
};
const zip = getZip(place.address_components);
PavelX
  • 1
0

i think this is the most accurate solution:

zipCode: result.address_components.find(item => item.types[0] === 'postal_code').long_name;
Suraj Rao
  • 29,388
  • 11
  • 94
  • 103
0

Just search for postal_code in all types, and return when found.

const address_components = [{"long_name": "2b","short_name": "2b","types": ["street_number"]}, { "long_name": "Louis Schuermanstraat","short_name": "Louis Schuermanstraat", "types": ["route"]},{"long_name": "Gent","short_name": "Gent","types": ["locality","political" ]},{"long_name": "Oost-Vlaanderen","short_name": "OV","types": ["administrative_area_level_2","political"]},{"long_name": "Vlaanderen","short_name": "Vlaanderen","types": ["administrative_area_level_1","political"]},{"long_name": "België","short_name": "BE","types": ["country","political"]},{"long_name": "9040","short_name": "9040","types": ["postal_code"]}];
    
// address_components = results[0]address_components

console.log({
  'object': getByGeoType(address_components),
  'short_name': getByGeoType(address_components).short_name,
  'long_name': getByGeoType(address_components).long_name,
  'route': getByGeoType(address_components, ['route']).long_name,
  'place': getByGeoType(address_components, ['locality', 'political']).long_name
});


function getByGeoType(components, type = ['postal_code']) {
  let result = null;
  $.each(components,
    function() {
      if (this.types.some(r => type.indexOf(r) >= 0)) {
        result = this;
        return false;
      }
    });
  return result;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Aldo
  • 730
  • 8
  • 20