302

I have tried looking on the Mozilla JSON stringify page of their docs as well as here on SO and Google but found no explanation. I have used JSON.stringify many time but never come across this result.

I have an array of JSON objects:

[
    {
        "param_2": "Description 1",
        "param_0": "Name 1",
        "param_1": "VERSION 1"
    },
    {
        "param_2": "Description 2",
        "param_0": "Name 2",
        "param_1": "VERSION 2"
    },
    {
        "param_2": "Description 3",
        "param_0": "Name 3",
        "param_1": "VERSION 3"
    }
]

It is attached to my $scope. In order to POST them as one parameter, I used the JSON.stringify() method and receive the following:

   [
        {
            "param_2": "Description 1",
            "param_0": "Name 1",
            "param_1": "VERSION 1",
            "$$hashKey": "005"
        },
        {
            "param_2": "Description 2",
            "param_0": "Name 2",
            "param_1": "VERSION 2",
            "$$hashKey": "006"
        },
        {
            "param_2": "Description 3",
            "param_0": "Name 3",
            "param_1": "VERSION 3",
            "$$hashKey": "007"
        }
    ]

I am just curious about what the $$hashkey property is exactly, as I expected something more similar to the following from the stringify method (that is, without the $$hashkey):

[
    {
        "1":{
            "param_2": "Description 1",
            "param_0": "Name 1",
            "param_1": "VERSION 1"
        },
         "2":{
            "param_2": "Description 2",
            "param_0": "Name 2",
            "param_1": "VERSION 2"
        },
         "3":{
            "param_2": "Description 3",
            "param_0": "Name 3",
            "param_1": "VERSION 3"
        }
    }
]

I am not sure if it is a factor, but I am using the following:

  • Angularjs 1.1.5,
  • JQuery 1.8.2
  • Spring 3.0.4

I'm also using Spring security 3.0.7 on the Server side.

It is not causing me any issues, but I would like to know the cause and reason for the $$hashkey

ruffin
  • 16,507
  • 9
  • 88
  • 138
jonnie
  • 12,260
  • 16
  • 54
  • 91

8 Answers8

552

Angular adds this to keep track of your changes, so it knows when it needs to update the DOM.

If you use angular.toJson(obj) instead of JSON.stringify(obj) then Angular will strip out these internal-use values for you.

Also, if you change your repeat expression to use the track by {uniqueProperty} suffix, Angular won't have to add $$hashKey at all. For example

<ul>
    <li ng-repeat="link in navLinks track by link.href">
        <a ng-href="link.href">{{link.title}}</a>
    </li>
</ul>

Just always remember you need the "link." part of the expression - I always tend to forget that. Just track by href will surely not work.

David Boike
  • 18,545
  • 7
  • 59
  • 94
  • Are there any performance tests about «track by» vs «$$hashKey»? (UPD. Ok, I've googled it and «track by» is more preferrable) – artuska Sep 17 '15 at 14:25
  • @artuska tracking by id is very straightforward as no hashes have to be computed you just reuse existing ids or increment a counter... – Christophe Roussy Oct 23 '15 at 13:29
  • 3
    and if you have a filter to apply, here is the correct order: `item in somelist | filter:somefilter track by item.key`, don't write the filter at the end of the line ! – Lewen Mar 22 '16 at 21:16
  • 1
    Note! I was using an array with a clone method which copied then inserted elements into an array, which was then rendered by an ng-repeat. I was getting angular 'duplicate key' errors when using JSON.parse(JSON.stringify(obj)) to clone my element. Using JSON.parse(angular.toJson(obj)); fixed things. Thanks! – SAL Nov 16 '16 at 15:17
  • 1
    You can also use the One Time Binding feature using double colon :: to prevent it from updating if you are only displaying data. {{::link.title}} – Philip E Mar 15 '17 at 05:07
  • Also, if you change your repeat expression to use the track by {uniqueProperty} suffix, Angular won't have to add $$hashKey at all. this line resolve many problems for me :) :) thanks – Gitesh Purbia Jan 17 '18 at 05:19
  • @David, Hi David, I have one clarification that Is there any possibility to $$haskey goes duplication? I have used JSOn.stringify(Obj) and I am able to see duplicate key in DB. – Md Aslam Jan 08 '19 at 09:55
73

In my use case (feeding the resulting object to X2JS) the recommended approach

data = angular.toJson(source);

help to remove the $$hashKey properties, but the result could then no longer be processed by X2JS.

data = angular.copy(source);

removed the $$hashKey properties as well, but the result remained usable as a parameter for X2JS.

Mosh Feu
  • 28,354
  • 16
  • 88
  • 135
rob2universe
  • 7,059
  • 39
  • 54
37

It comes with the ng-repeat directive usually. To do dom manipulation AngularJS flags objects with special id.

This is common with Angular. For example if u get object with ngResource your object will embed all the resource API and you'll see methods like $save, etc. With cookies too AngularJS will add a property __ngDebug.

Thomas Pons
  • 7,709
  • 3
  • 37
  • 55
24

If you don't want to add id's to your data, you could track by the index in the array, which will cause the items to be keyed by their position in the array instead of their value.

Like this:

var myArray = [1,1,1,1,1];

<li ng-repeat="item in myArray track by $index">
Michael Falck Wedelgård
  • 2,943
  • 1
  • 27
  • 39
8

If you are using Angular 1.3 or above, I recommend that you use "track by" in your ng-repeat. Angular doesn't add a "$$hashKey" property to the objects in your array if you use "track by". You also get performance benefits, if something in your array changes, angular doesn't recreate the entire DOM structure for your ng-repeat, it instead recreates the part of the DOM for the values in your array that have changed.

Ajay Ullal
  • 378
  • 2
  • 10
4

Update : From Angular v1.5, track by $index is now the standard syntax instead of using link as it gave me a ng-repeat dupes error.

I ran into this for a nested ng-repeat and the below worked.

<tbody>
    <tr ng-repeat="row in data track by $index">
    <td ng-repeat="field in headers track by $index">{{row[field.caption] }}</td>
</tr>
Vinay
  • 954
  • 8
  • 13
  • Just to clarify - attribute used in track by expression have to be unique across repeated collection. $index is one option. In most cases it is sufficient, but sometimes you might find useful to track by unique attribute.(id,...) – Martin Hlavňa Jul 04 '17 at 10:47
  • That requires assumption that order of your items will never change. :) – neatcoding Feb 21 '20 at 12:43
4

Here is how you can easily remove the $$hashKey from the object:

$scope.myNewObject = JSON.parse(angular.toJson($scope.myObject))

$scope.myObject - Refers to the Object that you want to perform the operation upon i.e. remove the $$hashKey from

$scope.myNewObject - Assign the modified original object to the new object so it can be used as necessary

Devner
  • 6,825
  • 11
  • 63
  • 104
  • I find this unnecessarily complex. You could just remove that single field - or every field starting with $. But probably you don't need to -- see the other answers. – sevcsik Oct 02 '17 at 05:10
1

https://www.timcosta.io/angular-js-object-comparisons/

Angular is pretty magical the first time people see it. Automatic DOM updates when you update a variable in your JS, and the same variable will update in your JS file when someone updates its value in the DOM. This same functionality works across page elements, and across controllers.

The key to all of this is the $$hashKey Angular attaches to objects and arrays used in ng-repeats.

This $$hashKey causes a lot of confusion for people who are sending full objects to an API that doesn't strip extra data. The API will return a 400 for all of your requests, but that $$hashKey just wont go away from your objects.

Angular uses the $$hashKey to keep track of which elements in the DOM belong to which item in an array that is being looped through in an ng-repeat. Without the $$hashKey Angular would have no way to apply changes the occur in the JavaScript or DOM to their counterpart, which is one of the main uses for Angular.

Consider this array:

users = [  
    {
         first_name: "Tim"
         last_name: "Costa"
         email: "tjsail33@gmail.com"
    }
]

If we rendered that into a list using ng-repeat="user in users", each object in it would receive a $$hashKey for tracking purposes from Angular. Here are two ways to avoid this $$hashKey.

alfishan aqeel
  • 220
  • 2
  • 5