3

I've got a JSON objects array, from which I want to get a couple of random values. I did wrote some code myself, and it works in the end, but it's to ugly to even show.

So that's why I started this question. What should be good/nice ways to code the following situation?

We've got an JSON array like this: (it's longer actually, but just a couple as example)

"features" : [
    {
      "attributes" : {
        "OBJECTID" : 6, 
        "Type" : "Gebied"
      }
    }, 
    {
      "attributes" : {
        "OBJECTID" : 70, 
        "Type" : "Water"
      }
    }, 
    {
      "attributes" : {
        "OBJECTID" : 80, 
        "Type" : "Water"
      }
    }, 
    {
      "attributes" : {
        "OBJECTID" : 91, 
        "Type" : "Land"
      }
    }, 
    {
      "attributes" : {
        "OBJECTID" : 66, 
        "Type" : "Gebied"
      }
    }, 
    {
      "attributes" : {
        "OBJECTID" : 78, 
        "Type" : "Land"
      }
    }
]

From that array we want to create a new simple array, which contains, for example:

  • 2 features with "type" = "Gebied"
  • 1 feature with "Type" = "Land"

Actually the number of features to select, which in this example are 1 and 2, can differ (up to 20 for one single type).

And most importantly, those features should be selected random.

I'm curious which approaches you guys would take and hopefully it helps to create a real nice block of code to do this, instead of the almost 100 rules of code I used now (and not even finished).

Dennis Hunink
  • 583
  • 2
  • 7
  • 21

3 Answers3

1

Not sure this is what you want, if not, I'll just delete it.. but here it goes:

var gebied = 0;
var id = new Array();

for(var i = 0; i < features.length; i++)
{
  if(features[i].attributes.Type == 'Gebied')
  {
    // saves the gebied instance +1
    id[gebied] = features[i].attributes.OBJECTID;
    gebied++;
  }
}

// pick random 2 from gebied array
var id1;
var id2;
var idListLength = id.length;

id1 = id[Math.floor(Math.random() * idListLength)];

if (idListLength > 1) {
    do {
      id2 = id[Math.floor(Math.random() * idListLength)];
    } while(id1 == id2);
}

// if it's just one random pick from array
var id1 = id[Math.floor(Math.random() * id.length)];

UPDATE

To have an input given number determine the number of random ids to pick:

function getRandomArrayElements(arr, count) {
    var randoms = [], clone = arr.slice(0);
    for (var i = 0, index; i < count; ++i) {
        index = Math.floor(Math.random() * clone.length);
        randoms.push(clone[index]);
        clone[index] = clone.pop();
    }
    return randoms;
}

function pickRandom(count)
{
  var gebied = 0;
  var id = new Array();

  for(var i = 0; i < features.length; i++)
  {
    if(features[i].attributes.Type == 'Gebied')
    {
      // saves the gebied instance +1
      id[gebied] = features[i].attributes.OBJECTID;
      gebied++;
    }
  }

  return getRandomArrayElements(id, count);
}

Example:

pickRandom($('#random').val());
Fabi
  • 973
  • 7
  • 18
  • Ow men, I like your approach a LOT! Thanks. There's one more thing: the number of items to select are variable also, actually. So (based on a input box) sometimes I would like to select 3 instances of "gebied", while another time I would like to select 15 of them. I added in in the question as well. So I think theres some modification needed in the last part. Any thoughts? – Dennis Hunink May 07 '13 at 21:47
  • I did a little research and here's an updated code for your situation http://fiddle.jshell.net/XsYgy/1/ ... if it's correct, I'll add to the main answer. – Fabi May 07 '13 at 21:48
  • As in the jsFiddle it absolutely looks like what i'm trying to achieve. So if you would add it to your answer, that would be great. I'll test it furthermore tomorrow and then get back to you, most likely by accepting your answer. For which I'm most thankful! – Dennis Hunink May 07 '13 at 21:52
  • if you would have another field with the type of feature, you can add it just as easy too, I've updated the fiddle with that – Fabi May 07 '13 at 22:02
0

I would not code it from scratch but use one of the rich libraries available, like underscore:

var gebied = _.filter(features, function(f) {
  return f.attributes.type === 'Gebied';
});

var result = [];
result.push(gebied[_.random(0, gebied,length)])

This is just one bit, but if this is what you mean then the rest is straightforward.

Alberto Zaccagni
  • 30,779
  • 11
  • 72
  • 106
0

Here is more of a functional approach to the problem, which has the advantage that it sticks to the DRY principle and yields quite readable and reusable code. Basically, a pair of filters does all the work:

function isType(t) { // filter by Type
  return function (el) {
    return el.attributes.Type === t;
  }
}

function chooseR(r) { // filter for choosing r of length
  var found = 0;

  return function (el, idx, arr) {
    // calculate probability to keep [# needed / # left]
    var keep = Math.random() < (r - found) / (arr.length - idx);

    // increment if keeping
    keep && found++;

    return keep;
  }
}

var myGebied = features.filter(isType('Gebied')).filter(chooseR(2)),
    myLand = features.filter(isType('Land')).filter(chooseR(1));

The chooseR algorithm is just a filter adaptation of the one in the answer to Select a random N elements from List. Obviously, chooseR(1) is stupid, but I just kept it to show the principle of the approach.

If you don't care about IE8, Array.prototype.filter is standard ES5 spec (see browser support). Otherwise, make sure to pick up a shim for that somewhere (the MDN page linked to has one at the bottom).

Community
  • 1
  • 1
merv
  • 67,214
  • 13
  • 180
  • 245