40

I have my JSON as follows

{"DATA": [{"id":11,"name":"ajax","subject":"OR","mark":63},
{"id":12,"name":"javascript","subject":"OR","mark":63},
{"id":13,"name":"jquery","subject":"OR","mark":63},
{"id":14,"name":"ajax","subject":"OR","mark":63},
{"id":15,"name":"jquery","subject":"OR","mark":63},
{"id":16,"name":"ajax","subject":"OR","mark":63},
{"id":20,"name":"ajax","subject":"OR","mark":63}],"COUNT":"120"}

Is there any good method to find out the distinct name from this JSON

Result javascript,jquery,ajax

I can do this using following methode

var arr=[''];
var j=0;
for (var i = 0; i < varjson.DATA.length; i++) {
  if($.inArray(varjson.DATA[i]["name"],arr)<0){
      arr[j]=varjson.DATA[i]["name"];
      j++;
  }
}

Is there any better method which gave me better performance?

Nithesh Narayanan
  • 11,481
  • 34
  • 98
  • 138

8 Answers8

70

I would use one Object and one Array, if you want to save some cycle:

var lookup = {};
var items = json.DATA;
var result = [];

for (var item, i = 0; item = items[i++];) {
  var name = item.name;

  if (!(name in lookup)) {
    lookup[name] = 1;
    result.push(name);
  }
}

In this way you're basically avoiding the indexOf / inArray call, and you will get an Array that can be iterate quicker than iterating object's properties – also because in the second case you need to check hasOwnProperty.

Of course if you're fine with just an Object you can avoid the check and the result.push at all, and in case get the array using Object.keys(lookup), but it won't be faster than that.

ZER0
  • 24,846
  • 5
  • 51
  • 54
  • it takes almost same time as mine. Here is the comparison http://jsfiddle.net/MsYGJ/1/ .Among these sberry method gave better performance. – Nithesh Narayanan Jul 22 '13 at 06:34
  • Could you please post the jsfiddle with also my method as comparison? Because in my test this approach is definitely faster than your original one, and it seems faster than sberry one too – but I'd like to double check. It could be that I'm missing something, or you have implemented in a different way. – ZER0 Jul 22 '13 at 06:45
  • Well, you have to remove the alert at line 354. Without it on my Firefox / Chrome / Safari is almost instantaneous, compare to the other to approaches; as also my test shown. – ZER0 Jul 22 '13 at 06:49
  • 1
    @ZER0 +1 for the way you iterated through an array. – sam Jan 15 '17 at 09:28
  • Not to be a Debbie Downer, but almost 5 years later this popped back up in my feed because of an upvote. @ZERO the problem with your fiddle is that you perform your method exactly once, while my approach is iterated 100,000 times. This is an updated version that shows my method is approx 3 times faster: http://jsfiddle.net/MsYGJ/113/. Of course optimization in this sort of time scale is seldom needed. – sberry Mar 22 '18 at 05:38
22

Underscore.js is great for this kind of thing. You can use _.countBy() to get the counts per name:

data = [{"id":11,"name":"ajax","subject":"OR","mark":63},
        {"id":12,"name":"javascript","subject":"OR","mark":63},
        {"id":13,"name":"jquery","subject":"OR","mark":63},
        {"id":14,"name":"ajax","subject":"OR","mark":63},
        {"id":15,"name":"jquery","subject":"OR","mark":63},
        {"id":16,"name":"ajax","subject":"OR","mark":63},
        {"id":20,"name":"ajax","subject":"OR","mark":63}]

_.countBy(data, function(data) { return data.name; });

Gives:

{ajax: 4, javascript: 1, jquery: 2} 

For an array of the keys just use _.keys()

_.keys(_.countBy(data, function(data) { return data.name; }));

Gives:

["ajax", "javascript", "jquery"]
Chris Seymour
  • 83,387
  • 30
  • 160
  • 202
  • Is it possible to use this countBy method selecting a pair of fields? For example, I would like to have the number of occurrences for each key / date. http://stackoverflow.com/questions/34075039/counting-number-of-elements-with-desired-conditions-in-json/34075630#34075630 Thank you, @iiSeymour. – pceccon Dec 07 '15 at 16:59
18

Use Jquery Method unique.

var UniqueNames= $.unique(data.DATA.map(function (d) {return d.name;}));

alert($.unique(names));

JSFiddle

Hardik Sondagar
  • 4,347
  • 3
  • 28
  • 48
  • this having a problem. If the name is repeated more than twice in the result that name appears twice. eg :if ajax comes 3 times then result contain `2 ajax` – Nithesh Narayanan Jul 22 '13 at 06:30
  • check jsfiddle it's not working as you say...I have put all name "ajax" and result showing only one Ajax. Check [JSFiddle](http://jsfiddle.net/hardiksondagar/QCx4a/) – Hardik Sondagar Jul 22 '13 at 06:35
  • nop. please check here http://jsfiddle.net/MsYGJ/3/ . Also it takes more time than other methods – Nithesh Narayanan Jul 22 '13 at 06:39
15

This is a great spot for a reduce

var uniqueArray = o.DATA.reduce(function (a, d) {
       if (a.indexOf(d.name) === -1) {
         a.push(d.name);
       }
       return a;
    }, []);
Evan Borden
  • 281
  • 1
  • 4
  • it takes more time than sberry's method. – Nithesh Narayanan Jul 22 '13 at 06:45
  • Yes there was no reason for the map in there. A single reduce could do it. You should be aware that @sberry's solution does not check hasOwnProperty. Stray properties from the prototype could find their way in to your list. – Evan Borden Jul 22 '13 at 13:59
9

First we can just run map() function to get the new array with the results of calling a provided function on every element in the varjson.DATA.

varjson.DATA.map(({name})=>name))

After getting the array of name from the varjson.DATA. We can convert it into a set that will discard all duplicate entries of array and apply spread operator to get a array of unique names:

[...new Set(varjson.DATA.map(({name})=>name))]

const varjson = {
  "DATA": [{
      "id": 11,
      "name": "ajax",
      "subject": "OR",
      "mark": 63
    },
    {
      "id": 12,
      "name": "javascript",
      "subject": "OR",
      "mark": 63
    },
    {
      "id": 13,
      "name": "jquery",
      "subject": "OR",
      "mark": 63
    },
    {
      "id": 14,
      "name": "ajax",
      "subject": "OR",
      "mark": 63
    },
    {
      "id": 15,
      "name": "jquery",
      "subject": "OR",
      "mark": 63
    },
    {
      "id": 16,
      "name": "ajax",
      "subject": "OR",
      "mark": 63
    },
    {
      "id": 20,
      "name": "ajax",
      "subject": "OR",
      "mark": 63
    }
  ],
  "COUNT": "120"
}

console.log( [...new Set(varjson.DATA.map(({name})=>name))]);
nircraft
  • 8,242
  • 5
  • 30
  • 46
hawryschuk
  • 91
  • 1
  • 2
6

As you can see here, when you have more values there is a better approach.

http://jsfiddle.net/MsYGJ/

temp = {}
// Store each of the elements in an object keyed of of the name field.  If there is a collision (the name already exists) then it is just replaced with the most recent one.
for (var i = 0; i < varjson.DATA.length; i++) {
    temp[varjson.DATA[i].name] = varjson.DATA[i];
}
// Reset the array in varjson
varjson.DATA = [];
// Push each of the values back into the array.
for (var o in temp) {
    varjson.DATA.push(temp[o]);
}

Here we are creating an object with the name as the key. The value is simply the original object from the array. Doing this, each replacement is O(1) and there is no need to check if it already exists. You then pull each of the values out and repopulate the array.

NOTE
For smaller arrays, your approach is slightly faster.

NOTE 2
This will not preserve the original order.

sberry
  • 128,281
  • 18
  • 138
  • 165
2

try this, MYJSON will be your json data.

var mytky=[];
mytky=DistinctRecords(MYJSON,"mykeyname");

function DistinctRecords(MYJSON,prop) {
  return MYJSON.filter((obj, pos, arr) => {
    return arr.map(mapObj => mapObj[prop]).indexOf(obj[prop]) === pos;
 })
}
Shailesh Tiwari
  • 295
  • 4
  • 4
  • I tried this but I don't get an array of distinct key values (values of attribute "mykeyname"); I get an array with the right number of elements, and one for each distinct value of "mykeyname", but the mytky array holds entire objects, not just key values (e.g., objects like {"id":11,"name":"ajax","subject":"OR","mark":63} from the OP, not just elements like 11). Also am I right that this iterates N-squared times for N objects in MYJSON array? I think so: .filter() iterates over all elements, and for each iteration .map() does the same, right? – GISmatters Apr 07 '20 at 16:15
0

Give this a go:

var distinct_list 

  = data.DATA.map(function (d) {return d[x];}).filter((v, i, a) => a.indexOf(v) === i)
mas_inri
  • 1
  • 1