1

I receive data as a JSON object, like this:

[{"transid":1091, "payee":"McDonalds", "amount":-549},
 {"transid":1092, "payee":"McDonalds", "amount":-342},
 {"transid":1093, "payee":"McDonalds", "amount":371}]

I know I can access the data like this:

alert(obj[0].amount);

But I would like to be able to access the data like this:

obj[transid].amount

where transid is a previously declared and assigned variable, like this:

var transid = 1091;
alert(obj[transid].amount);  //returns -549

If this is even possible, I assume the JSON object would have to be restructured (I don't have any control over how I receive the JSON object), but I don't really have any idea how to go about this. I've tried Googling and SOing, but I am just not sure what to look for.

Edit: I've looked at the proposed duplicate question as suggested by Travis J, and I do not agree that this is a duplicate. I'm not asking to loop through data. I'm asking for methods to reference by a specific index, given JSON that I don't control how it comes to me. The accepted answer in the proposed duplicate shows how I envision the code to look (in the second code box), but I don't think it really answers my question. Another answer in the proposed duplicate, posted by Hakan Bilgin, suggests using defiantjs, which would probably work. However, there are many other methods, some of which have been provided as answers to this question already.

mrphuzz
  • 189
  • 2
  • 8
  • This question is not exactly about simply finding things in JavaScript objects, but about a **syntax** for doing so. There are reasonably interesting approaches to making things work like the OP suggests, including both some preprocessing but also things like object proxies, which are not addressed by the supposed duplicate. Should re-open. –  Apr 29 '15 at 19:06
  • Travis J's answer to the duplicate question (currently 2nd, not accepted) has a brute force search method. It looks pretty well made, but the asker is interested in getting data sorted by index. The duplicate question's top answer covers what to do with indexed data, but not how to convert. [This question](http://stackoverflow.com/questions/4215737/) has some code for a starting point, but not an exact duplicate by any means. Voting to reopen. – Patrick M Apr 29 '15 at 19:46
  • @bruno and ekuusela: Thanks for the responses. I wish I could mark both as accepted answers! I like the .filter method better than the .forEach method, but so far this is only because I don't yet know the overhead of either solution. Let's say I have 100000 elements. Which would have less of a time impact? Restructuring the 100000 elements then accessing the data directly, as per ekuusela, or filtering the data as per bruno? I'll test later when I get home, but I appreciate both answers. I'll accept one later. – mrphuzz Apr 29 '15 at 20:22

2 Answers2

3

You can use filter like

obj.filter(function(o){
    return o['transid'] === 1091;
})[0].amount // -549

You can add the above function to Array's prototype like

Array.prototype.get = function(id){
    return Array.prototype.filter.call(this,function(obj){
        return obj['transid'] === id;
    })[0].amount
}

And use it like

obj.get(1091); // -549

It is up to you to add proper validation like dealing with not found keys and duplicates.

Bruno Calza
  • 2,732
  • 2
  • 23
  • 25
  • 1
    As written, this throws an exception if the caller asks for a transid that isn't in the collection. Otherwise, this is the easiest method. – McCroskey Apr 29 '15 at 19:05
  • `filter` keeps iterating even after the correct object is found. You might want to use `some` instead so you can break the loop. – ekuusela Apr 29 '15 at 19:59
  • @bruno, thanks for the reply. I followed the lead from ekuusela's comment above about `.some`. My understanding from reading is that `.some` returns true/false, which I don't think would work with your answer. However, using the `.find` polyfill, I think that works as intended. Thanks! – mrphuzz Apr 29 '15 at 22:53
  • with `some`, you'd have to assign the found value to some other variable and use that instead of the return value of `some`. Using `find` is better if you have the polyfill. `var result; a.some(function(e) { if (e.id === id) { result = e; return true; }});` – ekuusela Apr 30 '15 at 04:27
2

You can create a new object from that one. Something like this

var transactionArray = [
  {"transid":1091, "payee":"McDonalds", "amount":-549},
  {"transid":1092, "payee":"McDonalds", "amount":-342},
  {"transid":1093, "payee":"McDonalds", "amount":371}
];

var transactionsById = {};
transactionArray.forEach(function(element) {
    transactionsById[element.transid] = element;
});

var transid = 1091;
alert(transactionsById[transid].amount); //returns -549

Where transactionArray is what you refer to as obj in your question.

ekuusela
  • 5,034
  • 1
  • 25
  • 43
  • This is what the asker needed that wasn't in the duplicate question. +1 – Patrick M Apr 29 '15 at 19:54
  • Thanks for the help on this. I've played with both your and Bruno's answer. Yours does provide me the syntactical solution I sought. I do, however, like the `Array.prototype` solution (I ended up using `.find` instead of `.filter`). So I will have to feel it out more... which cake do I choose? Chocolate with chocolate sprinkles, or triple chocolate with no sprinkles? – mrphuzz Apr 29 '15 at 22:49