0

Situation:

I have an object like

{ prop_1 : val_1, prop_2 : val_2, prop_3 : val_3 , ..., prop_N : val_N } 

and I want to remove all properties that aren't prop_i, prop_j or prop_K ?

What is the best way to do this other than the "brute force" way of

var original = { prop_1 : val_1, prop_2 : val_2, prop_3 : val_3 , ..., prop_N : val_N };
var newguy = { prop_i : original.prop_i, prop_j : original.prop_j, prop_k : original.prop_k };
original = newguy;

????

Subpar Web Dev
  • 3,036
  • 5
  • 15
  • 32
  • 2
    you can `delete` all others, but i have to think that making a new one with 3 explicit props is simpler – dandavis Dec 23 '15 at 01:52
  • 1
    What issue do you have with doing it the "brute force" way? – James Montagne Dec 23 '15 at 01:57
  • if you use a buiilt in method, it will still do some iteration behind the scene..how else will it find the property property?...I would like to know this!...... – repzero Dec 23 '15 at 01:58
  • If you are not opposed to using a library, `lodash` or `underscore` has a `pick` to do this. – swestner Dec 23 '15 at 02:07
  • See http://stackoverflow.com/questions/29811147/whitelist-a-set-of-properties-from-a-multidimensional-json-array-and-delete-the, http://stackoverflow.com/questions/208105/how-to-remove-a-property-from-a-javascript-object, –  Dec 23 '15 at 02:52

4 Answers4

1

Well you can do a function to help you do that.

(function() {
  'use strict';

  function copyOnly(obj, keysToPreserve) {
    var result = {};
    for (var i = 0, length = keysToPreserve.length; i < length; ++i) {
      var key = keysToPreserve[i];
      result[key] = obj[key];
    }
    return result;
  }

  function copyExclude(obj, keysToExclude) {
    var result = {};
    for (var key in obj) {
      if (obj.hasOwnProperty(key) && keysToExclude.indexOf(key) === -1) { // -1 means key doesn't exist in keysToExclude
        result[key] = obj[key];
      }
    }
    return result;
  }

  var original = {
    a: '1',
    b: '2',
    c: '3',
    d: '4',
    e: '5'
  };

  var toPreserve = ['a', 'b', 'c'];
  var result1 = copyOnly(original, toPreserve);
  
  var toExclude = ['d', 'e'];
  var result2 = copyExclude(original, toExclude);
  
  // result1 will have the same structure as result2
  
  document.getElementById('result').innerHTML = 'result1 = ' + JSON.stringify(result1) + '\n' + 'result2 = ' + JSON.stringify(result2);
})();
<pre id="result"></pre>
rabbit.aaron
  • 1,648
  • 13
  • 21
0

Here is a non-brute-force way. It uses a whitelist, iterates over them, and copies values from "oldguy".

var oldguy = {
  "prop_1": 1,
  "prop_2": 2,
  "prop_3": 3,
  "prop_i": "i",
  "prop_j": "j",
  "prop_k": "k",
  "prop_N": "N",
  "prop_z": "Z"
};

var newguy = {};

var keys_to_include = ['prop_i', 'prop_j', 'prop_k'];

keys_to_include.forEach(function(k){
    newguy[k] = oldguy[k];
});

$('#output').html( JSON.stringify(newguy,null,'  ') );
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<pre><code id="output"></code></pre>
code_monk
  • 6,725
  • 2
  • 37
  • 34
  • a nested loop is rather slow. it would be better to cache the whitelist into an object with true values so that you can instantly check for acceptability without that slow indexOf() nested iteration on each outer iteration. – dandavis Dec 23 '15 at 02:04
  • you're absolutely right, @dandavis. i've modified my code to be more performant, and conform more closely to OP's requirement – code_monk Dec 23 '15 at 02:09
  • `forEach` won't work for older browsers and is slightly slower than the `for(var i =0 ... )` loop – rabbit.aaron Dec 24 '15 at 05:00
0

Simple .forEach and .indexOf over Object.keys to delete non-matches

function cleanExcept(o, whitelist) {
    Object.keys(o).forEach(k => whitelist.indexOf(k) !== -1 ? 0 : delete o[k]);
    return o;
}


var o = {foo: 'foo', bar: 'bar', fizz: 'fizz', buzz: 'buzz'};
cleanExcept(o, ['foo', 'fizz']); // Object {foo: "foo", fizz: "fizz"}

Using an Object cache instead of .indexOf, as per @dandavis

function cleanExcept(o, whitelist) {
    var w = {};
    whitelist.forEach(k => w[k] = true);
    Object.keys(o).forEach(k => w[k] ? 0 : delete o[k]);
    return o;
}

Modifying this cache just take the values you want and return it (i.e. you get a new object reference)

function cleanExcept(o, whitelist) {
    var w = {};
    whitelist.forEach(k => !(k in o) ? 0 : w[k] = o[k]);
    return w;
}
Community
  • 1
  • 1
Paul S.
  • 58,277
  • 8
  • 106
  • 120
  • nested loop is rather slow. it would be better to cache the whitelist into an object with true values so that you can instantly check for acceptability without that slow indexOf() nested iteration on each outer iteration. – dandavis Dec 23 '15 at 02:11
  • @dandavis wasn't sure about doing that because the question is then, why cache into a new object over just creating a new object and returning that – Paul S. Dec 23 '15 at 02:13
  • to achieve `whitelist[k] ? 0 : delete o[k]` instead of the indexOf(), i guess you can clobber the `true` of the cache to make an output if you're looking to avoid object creation for a game or something... the perf difference between property lookups and array iteration is not negligible... – dandavis Dec 23 '15 at 02:14
  • @dandavis I know what you mean, edited in these options, so we have (same ref) `O(nm)`, `O(n + m)` and a (new ref) `O(n)` method – Paul S. Dec 23 '15 at 02:32
0

In ES6, you can write

({prop_i, prop_j, prop_K}) => ({prop_i, prop_j, prop_K})(original)

This works by defining a function which deconstructs its arguments into certain property values, and returns an object with those values. Then call the function on the input object.

See One-liner to take some properties from object in ES 6.

Community
  • 1
  • 1