1

So I have an array of objects. Really the objects follow the GeoJSON specification, so keep that in mind. Within the "properties" objects, exists a proerty of "name". This name will be A, B, C... blah ... Z, AA, AB, etc. etc. for each different feature. See JSON example (I did strip out some other things which weren't important to this question, such as the geometry and what not...):

{
    "features" : [{
            "properties" : {
                "name" : "A",
                "description" : null,
            },
            "type" : "Feature"
        }, {
            "properties" : {
                "name" : "B",
                "description" : null,
            },
            "type" : "Feature"
        }, {
            "properties" : {
                "name" : "C",
                "description" : null,
            },
            "type" : "Feature"
        }
    ],
    "type" : "FeatureCollection"
}

What I'd like to do is find the MAX letter within this array of features to return the next one in the series. In this example, 'C' would be considered the MAX, so I should need to return the value of 'D'. If I had AA, that would be considered the MAX, and it would return 'AB'. If the max value happened to be a 'Z', I'd like to return a value of 'AA'.
Can ignore use of lowercase and only utilize 26, upper-cased english letters. No other characters.

I believe I could solve this with some usage of the javascript CharCodeAt(index) as well as applying Math.max, adding + 1, then converting back to it's ascii character represenation... but I'm having trouble bringing this together in a working function that loops through all this stuff.

Help would be appreciated!

Update: I got it partially working with the following code. However haven't quite figured out how to get it to work if it wraps around from Z to AA. Or if the MAX is found to be AF, then returning AG. AZ would have to return BA.

String.fromCharCode(Math.max.apply(Math,someObject.features.map(function(o){return o.properties.name.charCodeAt(0);})) + 1)

Other known rules:

  • Upper limit can be ZZ - highly unlikely I'd need to wrap back to AAA
  • The max character will not always be last in the array, so can't simply get the last feature of the array.
dvsoukup
  • 1,586
  • 15
  • 31
  • Will the MAX always just be the last object in your features array? – Ju66ernaut Jun 30 '16 at 17:32
  • Not necessarily... so you couldn't just snag the last element of the array – dvsoukup Jun 30 '16 at 17:32
  • *would be considered the MAX* - will MAX be the random input param? – RomanPerekhrest Jun 30 '16 at 17:33
  • @RomanPerekhrest the input param would be the entire object structure itself, and the result would be the next character in the series (ie, if F was found to be max, then G would be returned). But it's tricky when you hit Z and I need to wrap back around to AA. Or if it's AA, then return AB – dvsoukup Jun 30 '16 at 17:36
  • I suppose, you're indending the last `name` in the structure as a "MAX". What would be the ultimate possible "next character" in your case ... `ZZ`? – RomanPerekhrest Jun 30 '16 at 17:40
  • @RomanPerekhrest It would be incredibly unlikely that it would need to wrap around to triple digits... so it's probably safe to say we need only hit an upper limit of ZZ – dvsoukup Jun 30 '16 at 17:41
  • You could [sort the array by that property](http://stackoverflow.com/questions/1129216/sort-array-of-objects-by-string-property-value-in-javascript?rq=1), descending, then take the first one (to get the "max" one). Adding one to that should be easy. – Heretic Monkey Jun 30 '16 at 17:55

4 Answers4

1

The solution using Array.sort, String.fromCharCode and String.charCodeAt functions:

var someObject = {
    "features" : [{
            "properties" : { "name" : "AB", "description" : null},
            "type" : "Feature"
        }, {
            "properties" : {"name" : "B", "description" : null},
            "type" : "Feature"
        }, {
            "properties" : { "name" : "AF", "description" : null},
            "type" : "Feature"
        }
    ],
    "type" : "FeatureCollection"
};

function getNext(data) {
    data.features.sort(function(a,b){
        return a.properties.name.length - b.properties.name.length ||
                a.properties.name.localeCompare(b.properties.name);
    });

    var last = data.features[data.features.length - 1].properties.name;
    if (last.length === 1) {
        return (last === "Z")? "AA" : String.fromCharCode(last.charCodeAt(0) + 1);
    } else {
        if (last === "ZZ") return last;  // considering 'ZZ' as a limit
        if (last[1] !== "Z") {
            return last[0] + String.fromCharCode(last[1].charCodeAt(0) + 1);
        } else if (last[1] === "Z"){
            return  String.fromCharCode(last[0].charCodeAt(0) + 1) + "A";
        }
    }       
}

console.log(getNext(someObject));  // 'AG'
RomanPerekhrest
  • 88,541
  • 4
  • 65
  • 105
  • Thanks! I chose this one as it works for my needs. I'm sure some of the other answers work but this one just seemed best at the time. However there is one tiny issue with it in this this line needs to do a return... thus there's really no need to have a "next" variable: (last === "Z")? "AA" : String.fromCharCode(last.charCodeAt(0) + 1); Could you update your answer to reflect that? Otherwise it will return an undefined. – dvsoukup Jun 30 '16 at 21:29
  • @dvsoukup, you were right. I've fixed that thing. Check it out. Thanks – RomanPerekhrest Jul 01 '16 at 04:06
0

You can find the MAX string with:

var maxName = null,
    obj = null,
    name = null;
for(var idx = 0; idx < features.length; ++idx){
    obj = features[idx];
    name = obj.properties.name;
    if(maxName == null || name.length > maxName.length || 
        (name.length == maxName.length && name > maxName)
    ){
        maxName = name;
    }
}

I'm still working on getting the next name though.

Damián Castro
  • 337
  • 2
  • 12
0

I think you want to sort, which actually JS has a nice feature for comparing strings. However, it would return that ("AA" > "Z") === true so you want to account for length as well.

I think this works.

function sortName(a, b){
    a = a.properties.name;
    b = b.properties.name;
    if(a.length>b.length){
        return 1;
    }
    if(a > b){
        return 1;
    }
    return -1;
}

console.log(someObject.features.sort(sortName)[0]. properties.name);
LessQuesar
  • 3,123
  • 1
  • 21
  • 29
  • Sorting is an inefficient way of finding the maximum value in an array. Given that you have to write a comparison function anyway, you can simply run a single loop through the array and keep track of the maximum value and its index as you go. Of course for a small array the difference may not be significant, but for a larger array the single loop would be much faster than a sort. – Michael Geary Jun 30 '16 at 18:04
0

You can do it using

var obj = {
         "features": [{
             "properties": {
                 "name": "A",
                 "description": null,
             },
             "type": "Feature"
         }, {
             "properties": {
                 "name": "B",
                 "description": null,
             },
             "type": "Feature"
         }, {
             "properties": {
                 "name": "C",
                 "description": null,
             },
             "type": "Feature"
         }],
         "type": "FeatureCollection"
     };


     var largest = Math.max.apply(Math, findProp(obj.features, "name"));
     console.log(changeToStr(largest + 1));

Where findProp is getting the property value array, changeToStr is converting number to string and changeToNum is converting number to String.

function changeToNum(val) {
         var base = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
             i, j, result = 0;

         for (i = 0, j = val.length - 1; i < val.length; i += 1, j -= 1) {
             result += Math.pow(base.length, j) * (base.indexOf(val[i]) + 1);
         }
         return result;
     };


     function changeToStr(number) {
         var baseChar = ("A").charCodeAt(0),
             letters = "";

         do {
             number -= 1;
             letters = String.fromCharCode(baseChar + (number % 26)) + letters;
             number = (number / 26) >> 0;
         } while (number > 0);

         return letters;
     }

     function findProp(obj, key, out) {
         var i,
             proto = Object.prototype,
             ts = proto.toString,
             hasOwn = proto.hasOwnProperty.bind(obj);

         if ('[object Array]' !== ts.call(out)) out = [];

         for (i in obj) {
             if (hasOwn(i)) {
                 if (i === key) {
                     out.push(changeToNum(obj[i]));
                 } else if ('[object Array]' === ts.call(obj[i]) || '[object Object]' === ts.call(obj[i])) {
                     findProp(obj[i], key, out);
                 }
             }
         }

         return out;
     }

See the working Fiddle here.

Hector Barbossa
  • 5,506
  • 13
  • 48
  • 70