1

I've got a JSON response that looks like this:

{
    "COLUMNS":["SETTING_NAME","SETTING_VALUE","COLOR"],
    "DATA": [
                ["setting_1",100.0,"yellow"],
                ["setting_2",150.0,"red"],
                ["setting_3",30.0,"green"],
                ["setting_4",11.0,"blue"]
            ]
 }

How do I find the 'color' for the setting 'setting_4'? Acceptable solutions would either be am easy way to access the data, or a function to transform this into an exploded key/value array like

 [
     setting_1_value: '100', 
     setting_1_color: 'yellow', 
     setting_2_value: "150"
     ...
  ]
2
  • Do you want the value for setting_4 because you're arbitrarily picking that one, or because it's the last one/highest setting number? Commented Aug 29, 2011 at 22:40
  • Totally arbitrary. The actual data set is much larger. Commented Aug 29, 2011 at 22:46

5 Answers 5

3

You can use this code to put the data into the type of data structure that you asked for:

var response = {"COLUMNS":["SETTING_NAME","SETTING_VALUE","COLOR"],
"DATA":[["setting_1",100.0,"yellow"],["setting_2",150.0,"red"],
["setting_3",30.0,"green"],["setting_4",11.0,"blue"]]};

var data = response.DATA;
var columns = response.COLUMNS;
var hash = {}, item, name, i;
var cols = {};

// remember order of columns
for (i = 0; i < columns.length; i++) {
    cols[columns[i]] = i;
}
// fetch data from correct column
for (i = 0; i < data.length; i++) {
    item = data[i];
    name = item[cols["SETTING_NAME"]];
    hash[name + "_value"] = item[cols["SETTING_VALUE"]];
    hash[name + "_color"] = item[cols["COLOR"]];
}
hash.num = data.length;

As you requested, this gives you a data structure like this so you can directly read any value you want:

{
    "setting_1_value":100,
    "setting_1_color":"yellow",
    "setting_2_value":150,
    "setting_2_color":"red",
    "setting_3_value":30,
    "setting_3_color":"green",
    "setting_4_value":11,
    "setting_4_color":"blue",
    "num":4
}

jsFiddle here: http://jsfiddle.net/jfriend00/HZmYN/ that generated this result.

Personally, I would rather use this code to parse it into this type of data structure:

var response = {"COLUMNS":["SETTING_NAME","SETTING_VALUE","COLOR"],
"DATA":[["setting_1",100.0,"yellow"],["setting_2",150.0,"red"],
["setting_3",30.0,"green"],["setting_4",11.0,"blue"]]};

var data = response.DATA;
var columns = response.COLUMNS;
var newData = [], item, obj, i, num, match;

var cols = {};

// remember order of columns
for (i = 0; i < columns.length; i++) {
    cols[columns[i]] = i;
}

for (i = 0; i < data.length; i++) {
    item = data[i];
    obj = {};
    obj.value = item[cols["SETTING_VALUE"]];
    obj.color = item[cols["COLOR"]];
    obj.name = item[cols["SETTING_NAME"]];
    match = obj.name.match(/\d+$/);
    if (match && match.length > 0) {
        obj.settingNumber = parseInt(match[0], 10);
    }
    newData.push(obj);
}

// now sort the array by the number in the name setting
newData.sort(function(a, b) {
    return(a.settingNumber- b.settingNumber);
});

And generates this data structure:

[
  {"value":100,"color":"yellow","name":"setting_1","settingNumber":1},
  {"value":150,"color":"red","name":"setting_2","settingNumber":2},
  {"value":30,"color":"green","name":"setting_3","settingNumber":3},
  {"value":11,"color":"blue","name":"setting_4","settingNumber":4}
]

Illustrated in this jsFiddle: http://jsfiddle.net/jfriend00/A23Jd/.

The reason I prefer this structure, is you can more easily access the "n" settings as an array of objects:

newData[0].color
newData[0].value
newData[0].name
newData[1].color
....

And, it's easier to iterate through the various settings

Sign up to request clarification or add additional context in comments.

7 Comments

I think the "_value" and "_color" property suffixes are supposed to come from the COLUMNS array. Hardcoding them isn't robust enough.
It's a bit hard to know what the rules are here and what assumptions you can make. It's not practical to make no assumptions about what's in the data, because the OP can't even look for setting_4 if they don't assume there are settings with those names. We could add code to accept any column order, but it seems like you do have to know what column names to look for so perhaps we can assume the column names.
I like both of these examples for quite a few reasons- primarily for how readable they are, especially the way you access the objects afterwards. Thanks!
@jfriend - It's possible to do blind data restructuring where the data itself describes the target structure. The OP may still need to know which property names to look for when attempting to use the data elsewhere, but the restructuring algorithm can (and probably should) be robust enough to scale infinitely. And in some cases, the OP won't even need to know that (though I haven't seen much of that outside of internal-facing tools).
@Tom - updated the first piece of code to pull the column order from the COLUMNS array, though since he asked for an output data structure that "knows" what each value is, I assume he knows the columns he's looking for so this code makes that assumption. But, the columns can now be in any order. I'll update the second piece of code now.
|
2

Using $.grep will allow you to access the data without mapping them before:

var json={"COLUMNS":["SETTING_NAME","SETTING_VALUE","COLOR"],
          "DATA":[["setting_1",100.0,"yellow"],
                  ["setting_2",150.0,"red"],
                  ["setting_3",30.0,"green"],
                  ["setting_4",11.0,"blue"]]}

alert($.grep(json.DATA, function(item){return(item[0]=='setting_4');})[0][2])//returns 'blue'

//or using the labels provided by COLUMNS:

alert($.grep(json.DATA, 
             function(a){return(a[0]=='setting_4');})[0][$.inArray('COLOR',json.COLUMNS)])

4 Comments

You can probably do this without relying on jQuery if you substitute Array.prototype.filter for jQuery.grep & Array.prototype.indexOf for jQuery.inArray
Doesn't this assume a lot about the order of the data?
I better rely on jQuery because both methods are not supported by IE<9
@jfriend00: of course, but the 2nd example uses the key from the COLUMNS. I guess COLUMNS are not there for fun, so $.inArray('COLOR',json.COLUMNS) should always return the index of the color, no matter what order. If COLUMNS doesn't give the information where the color is placed and he can't rely on the order there is no logical way to find the color or anything else.
1

You can do this with a simple for loop:

var obj = {"COLUMNS":["SETTING_NAME","SETTING_VALUE","COLOR"],
"DATA":[["setting_1",100.0,"yellow"],["setting_2",150.0,"red"],
["setting_3",30.0,"green"],["setting_4",11.0,"blue"]]};

for(var i = 0; i < obj.DATA.length; i++)
{
    var row = obj.DATA[i]
    if (row[0] == 'setting_4')
    {
        console.log(row[2]);
        break;
    }
}

Prints:

blue

Comments

1

You could simply reduce that list of DATA:

DATA.reduce(function (value, item) { if (item[0] === "setting_4") return item[2] })

You could wrap that whole thing into a function for easier use, passing in the "setting_4" part. E.g.

var getColour = function (id) {
  return DATA.reduce(function (value, item) {
    if (item[0] === id) return item[2]
  })
}

UPDATE: you could zip the two lists together, perhaps that would make access easier?

obj['DATA'].map(function (row) {
  return obj['COLUMNS'].reduce(function (memo, columnName, index) {
    memo[columnName] = row[index]
    return memo
  }, {})
})

This will return something like the following:

[{
   COLOR: "yellow",
   SETTING_NAME: "setting_1",
   SETTING_VALUE: 100
}]

4 Comments

I love higher order functions like map and reduce, but you should point out that array.reduce is part of the Javascript 1.8 spec, so for incapable browsers, such as IE <= 8, you'll need to include an implementation of reduce.
Good point. It is easy to implement though, or you could use a library like augment.js
I think you'd want to save the benefits of any work done to translate the original data structure so future access is cheaper. That way we're not necessarily processing the entire data payload for a single value and throwing the rest away. I think using reduce forecloses on any such possibility.
Oh neat, I had never heard of that library before, good find.
1

A generic algorithm for translating the dataset into a more-easily-addressed structure.

var json = {
    "COLUMNS": [
        "SETTING_NAME",
        "SETTING_VALUE",
        "COLOR"],
    "DATA": [
        ["setting_1",100.0,"yellow"],
        ["setting_2",150.0,"red"],
        ["setting_3",30.0,"green"],
        ["setting_4",11.0,"blue"]
    ]
};

function translateJSON(json) {
    var oHash = {};

    var data = json['DATA'];
    var cols = json['COLUMNS'];

    for(var i = 0, imax = data.length; i < imax; i++) {
        var row = data[i]; // shorthand

        for(var j = 1, jmax = cols.length; j < jmax; j++) {
            var c = cols[j]; // shorthand
            oHash[(row[0] + '_' + c.replace(/[^_]+_/, '')).toLowerCase()] = row[j];
        }
    }

    return oHash;
}

var h = translateJSON(json);
console.log(h['setting_4_color']);

Edit: updated the code. translateJSON will transform the JSON into the data structure you described, for easier property access. If you anticipate needing to access more than one property from the same JSON payload, it will be much more efficient to do a one-time transform before data access than to use something like $.grep, and much less terse than doing the column-name cross-referencing by hand.

That said, I don't think the target data structure you asked for is necessarily the best one. Assuming you can't change the structure of the JSON payload, it would still probably be better to transform that into something like:

data = {
    'setting_1': { 'value': 100.0, 'color': 'yellow' },
    'setting_2': { 'value': 150.0, 'color': 'red' }
    ...
};

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.