10

My javascript object looks something like:

$scope.display = {
  current: {
       key1: 'value1',
       key2: ['a', 'b'],
       key3: 'value2'
    }
}

Upon some events in my code, I would like to reset these values to undefined like below:

$scope.display = {
  current: {
       key1: undefined,
       key2: [],
       key3: undefined
    }
}

I use libraries like lodash, however i don't see any function that would perform this. I know how to do this manually, but I was wondering if there is a "Best practices" way of performing this task.

Kos
  • 70,399
  • 25
  • 169
  • 233
runtimeZero
  • 26,466
  • 27
  • 73
  • 126
  • 3
    You want to reset an array to an empty array, but a string to undefined, not an empty string. What about objects? numbers? There's more than one way someone would like to do it – Kos Dec 17 '14 at 15:47
  • Unless you need to do this a lot, and have very specific rules (like all strings become undefined, etc.), manual seems best. – Alexander O'Mara Dec 17 '14 at 15:49
  • thank you all.. It seems there is no built in way..however the options seem between 1. Helper function 2. deep copy 3. Writing a custom function with a loop.. for now I am proceedign with a helper function way (suggested by @dfsq) since it just seems cleaner. – runtimeZero Dec 17 '14 at 16:10

11 Answers11

7

I would create a helper function returning object structure:

function getDisplayObject() {
    return {
        current: {
            key1: undefined, // or you can omit undefined keys
            key2: [],
            key3: undefined
        }
    };
}

$scope.display = getDisplayObject();

So later when you need to reset data you would execute $scope.display = getDisplayObject(); again.

dfsq
  • 191,768
  • 25
  • 236
  • 258
  • mmm yeah, maybe this is faster than what i answered :) – AndreaBogazzi Dec 17 '14 at 15:53
  • 1
    Note, any variables that still reference to the old `current` property will still point to the previous object. Not necessarily bad, but something to be aware of. +1 for the clean, library-independent solution. – Alexander O'Mara Dec 17 '14 at 16:01
  • 1
    This will give you a different object in the heap, its not really clearing an object. – Cody Mar 28 '17 at 20:49
  • @Cody Yes, exactly. Simple way to achieve "clearing" object by replacing with a new clean one. But of course it's not mutating original one. – dfsq Mar 28 '17 at 21:25
  • I would assume "clear" means preserve the original object, which is an important distinction in Angular. – gbtimmon Jun 20 '18 at 16:52
3

Here is my solution with lodash mapValues function:

var $clearObject = function(value) {
  if (_.isString(value)) {
    return undefined
  };
  if (_.isArray(value)) {
    return [];
  };
};

var $scopeDisplay = {
  current: {
    key1: 'value1',
    key2: ['a', 'b'],
    key3: 'value2'
  }
};

$scopeDisplay.current = _.mapValues($scopeDisplay.current, $clearObject);

console.log($scopeDisplay);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.min.js"></script>
Yauheni Prakopchyk
  • 10,202
  • 4
  • 33
  • 37
2

You would loop the properties of your object like this

for (var key in current){
    if (current.hasOwnProperty(key)){
        if (typeof current[key] === 'string'){
            current[key] = undefined;
        } else if (current[key] instanceof Array) {
            current[key] = [];
        } // else ???  Not sure how you want to handle other types
    }
}

Array check subject to some potential problems described in the comments here

Community
  • 1
  • 1
Adam Rackis
  • 82,527
  • 56
  • 270
  • 393
  • 1
    Why not `current[key] instanceof Array`? Also worth noting this replaces the array with a new one, versus emptying the old one (maybe preferable, maybe not). – Alexander O'Mara Dec 17 '14 at 15:50
  • @AlexanderO'Mara - you're mostly right. Apparently there are some edge cases where that can fail, but I updated my answer – Adam Rackis Dec 17 '14 at 15:53
  • 1
    There are some slight differences, such as inheritance and cross-frame code, but I would recommend `instanceof` for most code. Anyhow, +1. – Alexander O'Mara Dec 17 '14 at 15:57
  • This looks like the only way right.. I was hoping there was some built in function in Js, Jquery or lodash – runtimeZero Dec 17 '14 at 16:02
  • If you want a generic way that will work on ANY object, then yeah - I suspect you'll want to loop each property and reset in whichever way you need. – Adam Rackis Dec 17 '14 at 16:04
2

What about this?

// If you just want to reset a simple object
let reset = (obj) => {
  Object.keys(obj).map(key => {
    if (obj[key] instanceof Array) obj[key] = []
    else obj[key] = undefined
  })
}


// If you want to reset a complex object
let recursiveReset = (obj) => {
  Object.keys(obj).map(key => {
    // Test if it's an Object
    if (obj[key] === Object(obj[key])) {
      recursiveReset(obj[key])
      return
    }
    if (obj[key] instanceof Array) obj[key] = []
    else obj[key] = undefined
  })
}

// So you can simply use
reset($scope.display.current)
// or
recursiveReset($scope.display)

test link

Existe Deja
  • 1,227
  • 3
  • 14
  • 36
1

In my Angular controller, I do the following:

    $scope.user = {
        firstname: "",
        lastname: "",
        displayname: "",
        email: "",
        password: "",
        passwordConfirm: ""
    };

    // default state of the form
    $scope.default = angular.copy($scope.user);

    /**
     * Resets the form to its default state
     * @return {void}
     */
    $scope.reset = function () {
        $scope.user = angular.copy($scope.default);
    }

Initially the scope is empty, so I clone it, and whenever it needs reset, simply call a function. However, without knowing the scope of your project, it's hard to determine the best way to handle it.

Tristan Lee
  • 1,210
  • 10
  • 17
1

I did it in AngularJS controller as:

function item_object() {
    var list_item = {
        ID: '',
        Title: '',
        LastName: '',
        Contact: '',
        City: ''
    };

  return list_item;
}

//Define the object for the item
$scope.Item = item_object();

// Reset product details
$scope.clear = function () {
    $scope.Item = item_object();
}

In this way you do not keep a copy of empty or default object. So no extra cost.

  • Basically your proposal is to have a default object which can be used to replace the current object whenever needed. This will be handy in some situations and would not work in others, imagine you passed your object already around to other functions (contexts). If you now create a new instance of the default object those other functions (contexts) wont know about the change as their instance still exists. – pintxo Dec 23 '16 at 15:36
0

If you are using Jquery, like it seems you are doing, you can do that:

Preapre an empty template of your object:

var emptyTemplate = {
  current: {
       key1: undefined,
       key2: [],
       key3: undefined
    }
}

and then run:

 $scope.display = $.extend(true, {}, emptyTemplate);

If you want to automate it, define some data binding:

var defaults = {
 String:  undefined,
 Array: [],
 bool: false
}

Then loop in your object, like other suggestions:

function resetObj (obj) {
    _(obj).forEach(
        function(value, key, objRef) {
            if (_.isObject(value)) {
                resetObj(objRef[key]);
            } else {                    
              var myvarType = typeOf value;
              objRef[key] = defaults[myvarType];
            }
        }
    );
}  

I copied this nesting function from other answer, just to add my two cents.

AndreaBogazzi
  • 14,323
  • 3
  • 38
  • 63
0

There is no built in way. You would need to loop through and set the defaults based on type.

var x = {
    current: {
        key1: 'value1',
        key2: ['a', 'b'],
        key3: 'value2'
    }
};

_(x.current).forEach(
    function(value, key, obj) {
        var result = undefined;
        if (_.isArray(value)) {
            result = [];
        }
        obj[key] = result;
    }
);

console.log(x);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/2.4.1/lodash.js"></script>

Basic idea if you have nested objects

var x = {
        current: {
            key1: 'value1',
            key2: ['a', 'b'],
            key3: 'value2',
            xxxx:  {
                keyx1: 'valuex1',
                keyx2: ['xa', 'xb'],
                keyx3: 'valuex2'
            }         
        }
    };

function resetObj (obj) {
    _(obj).forEach(
        function(value, key, objRef) {
            var result = undefined;
            if (_.isObject(value)) {
                resetObj(objRef[key]);
            } else {                    
              if (_.isArray(value)) {
                  result = [];
              }
              objRef[key] = result;
            }
        }
    );

}  

resetObj(x)
console.log(x);
    <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/2.4.1/lodash.js"></script>
epascarello
  • 204,599
  • 20
  • 195
  • 236
0

A good way to deal with this would be to use a class instead of an object literal, like:

class current {
    constructor() {
        this.key1 = undefined;
        this.key2 = [];
        this.key3 = undefined;
    }
}

And whenever you want to clear all the values you can just create a new instance of this class, like:

$scope.display.current = new current();
Mendy
  • 7,612
  • 5
  • 28
  • 42
0
function isObject (value) {
  return value && typeof value === 'object' && value.constructor === Object;
}

const resetObj = (obj, resetVal) => {
  if(!isObject(obj)) return;
  for(let i in obj){
    if(!obj.hasOwnProperty(i)) continue;
    if(!isObject(obj[i])){
      obj[i] = resetVal;
    }else{
      resetObj(obj[i], resetVal);
    }
  }
};
He Huang
  • 1
  • 1
0

+++++++++++++++++++++++++Typescript solution+++++++++++++++++++++++++++

 private deviceSettingsToReset: RemoteDeviceSetting;
    
    // depp copy of response into deviceSettingsToReset variable which need to use in reset functionality in UI.
    this.deviceSettingsToReset = JSON.parse(JSON.stringify(response));
    
    // assign value from original copy.
    this.selectedAlgorithm = JSON.parse(JSON.stringify(this.deviceSettingsToReset));