ES6 dispose of the object destructuring left-over (object trimming) - javascript

One nice use-case of the destructuring feature when used with rest parameters is that you can get trimmed clones.
var source = { w1: 'val1', w2: 'val2', unwanted1: 'val3', unwanted2: 'val4'};
var {unwanted1, unwanted2, ...target} = source;
console.log(target); // `{ w1: 'val1', w2: 'val2' }` Exactly what you want
However, the side effect is that your scope is now polluted with two variables that you never care to use: unwanted1 and unwanted2.
If _ meant don't care, you could do something like this
var {
unwanted1:_, // throw away
unwanted2:_, // throw away
target
} = source;
However, in Javascript _ is a proper identifier.
If used once in that manner (unwanted: _), you'll end up with one unwanted variable called _, which goes against the goal.
If used more than once, like above, an error is issued:
SyntaxError: Identifier '_' has already been declared.
Is there any way I can throw away the undesired artifacts/variables of destructuring?
Of course, the following solutions are always available.
var target = {
w1: source.w1,
w2: source.w2,
}
and
var target = {...source};
delete target.unwanted1;
delete target.unwanted2;
However doing this with destructuring only seems to be the cleanest way if you're cloning an object with many parameters and you need to exclude just a couple.

Introducing _, __, ___, etc to drop 1,2,3 or more properties doesn't make much difference as it still creates the variables, which '...you will never care to use' and moreover, it threatens to add a flavor of spaghetti to your code.
However, since you need to indicate explicitly which exactly properties you want to drop, one may consider other object trimming techniques, e.g.
filter unwanted properties
const obj = {prop1: 1, prop2:2, prop3: 3, prop4: 4, prop5: 5},
keysToDrop = ['prop2', 'prop3', 'prop4'],
trimmedObj = Object.fromEntries(
Object
.entries(obj)
.filter(([key,val]) => !keysToDrop.includes(key)
)
)
console.log(trimmedObj)
.as-console-wrapper{min-height:100%;}
make use of Array.prototype.reduce(), which may even give you certain performance boost, compared to destructuring
const obj = {prop1: 1, prop2:2, prop3: 3, prop4: 4, prop5: 5},
keysToDrop = ['prop2', 'prop3', 'prop4'],
trimmedObj = Object
.keys(obj)
.reduce((r,key) =>
(!keysToDrop.includes(key) && (r[key] = obj[key]), r),{})
console.log(trimmedObj)
.as-console-wrapper{min-height:100%;}

Use _, __,___ or Just method to exclude: :D haha
function prop(source, excluded) {
if (source == null) return {};
var target = {};
var sourceKeys = Object.keys(source);
var key, i;
for (i = 0; i < sourceKeys.length; i++) {
key = sourceKeys[i];
if (excluded.indexOf(key) >= 0) continue;
target[key] = source[key];
}
return target;
}
var source = {
w1: "val1",
w2: "val2",
unwanted1: "val3",
unwanted2: "val4"
};
var target = prop(source, ["unwanted1", "unwanted2"]);

Is there any way I can throw away the undesired artifacts/variables of destructuring?
The only way that's also not too terrible would be to define a function that does the same thing:
const clone = ({unwanted1, unwanted2, ...target}) => target;
const target = clone(source);
The variables are still created but their visibility is limited to the function which terminates immediately.
However doing this with destructuring only seems to be the cleanest way if you're cloning an object with many parameters and you need to exclude just a couple.
The disadvantage of the above approach is that the function is specific to a specific object. You cannot reuse it for other objects. Sure, it's rather small so maybe that's not a big deal. But having a more generic helper function might be easier to understand.

Related

Is mutating accumulator in reduce function considered bad practice?

I'm new to functional programming and I'm trying rewrite some code to make it more functional-ish to grasp the concepts. Just now I've discovered Array.reduce() function and used it to create an object of arrays of combinations (I've used for loop before that). However, I'm not sure about something. Look at this code:
const sortedCombinations = combinations.reduce(
(accum, comb) => {
if(accum[comb.strength]) {
accum[comb.strength].push(comb);
} else {
accum[comb.strength] = [comb];
}
return accum;
},
{}
);
Obviously, this function mutates its argument accum, so it is not considered pure. On the other hand, the reduce function, if I understand it correctly, discards accumulator from every iteration and doesn't use it after calling callback function. Still, it's not a pure function. I can rewrite it like this:
const sortedCombinations = combinations.reduce(
(accum, comb) => {
const tempAccum = Object.assign({}, accum);
if(tempAccum[comb.strength]) {
tempAccum[comb.strength].push(comb);
} else {
tempAccum[comb.strength] = [comb];
}
return tempAccum;
},
{}
);
Now, in my understanding, this function is considered pure. However, it creates a new object every iteration, which consumes some time, and, obviously, memory.
So the question is: which variant is better and why? Is purity really so important that I should sacrifice performance and memory to achieve it? Or maybe I'm missing something, and there is some better option?
TL; DR: It isn't if you own the accumulator.
It's quite common in JavaScript to use the spread operator to create nice looking one-liner reducing functions. Developers often claim that it also makes their functions pure in the process.
const foo = xs => xs.reduce((acc, x) => ({...acc, [x.a]: x}), {});
//------------------------------------------------------------^
// (initial acc value)
But let's think about it for a second... What could possibly go wrong if you mutated acc? e.g.,
const foo = xs => xs.reduce((acc, x) => {
acc[x.a] = x;
return acc;
}, {});
Absolutely nothing.
The initial value of acc is an empty literal object created on the fly. Using the spread operator is only a "cosmetic" choice at this point. Both functions are pure.
Immutability is a trait not a process per se. Meaning that cloning data to achieve immutability is most likely both a naive and inefficient approach to it. Most people forget that the spread operator only does a shallow clone anyway!
I wrote this article a little while ago where I claim that mutation and functional programming don't have to be mutually exclusive and I also show that using the spread operator isn't a trivial choice to make.
Creating a new object on every iteration is common practice, and sometimes recommended, despite any potential performance issues.
(EDIT:) I guess that is because if you want to have only one general advice, then copying less likely causes
problems than mutating. The performance starts to become a "real" issue
if you have more than lets say about 1000 iterations. (For more details see my update below)
You can make your function pure in e.g. in this way:
const sortedCombinations = combinations.reduce(
(accum, comb) => {
return {
...accum,
[comb.strength]: [
...(accum[comb.strength] || []),
comb
]
};
},
{}
);
Purity might become more important if your state and reducer is defined somewhere else:
const myReducer = (accum, comb) => {
return {
...accum,
[comb.strength]: [
...(accum[comb.strength] || []),
comb
]
};
};
const initialState = {};
const sortedCombinations = combinations.reduce( myReducer, initialState );
const otherSortedCombinations = otherCombinations.reduce( myReducer, initialState );
const otherThing = otherList.reduce( otherReducer, initialState );
Update (2021-08-22):
preface to this update
As stated in the comments (and also mentioned in the question), of course copying on every iteration is less performant.
And I admit that in many cases, technically I can't see any disadvantages of mutating the accumulator (if you know what you are doing!).
Actually, thinking about it again, inspired from the comments and other answers,
I changed my mind a bit, and will consider mutating more often now, maybe at least
where I don't see any risk that e.g. somebody else misunderstands my code later.
But then again the question was explicitly about purity ... anyway, so here some more details:
purity
(Disclaimer: I must admit here that I know about React, but I don't know much about "the world of functional programming"
and their arguments about the advantages, e.g. in Haskell)
Using this "pure" approach is a tradeoff. You loose performance, and you win easier understandable and less coupled code.
E.g. in React, with many nested Components, you can always rely on the consistent state of the current component.
You know it will not be changed anywhere outside, except if you have passed down some 'onChange' callback explicitly.
If you define an object, you know for sure it will always stay unchanged.
If you need a modified version, you would have an new variable assignment,
this way it is obvious that you are working with a new version of the data
from here down, and any code that might use the old object will not be affected.:
const myObject = { a1: 1, a2: 2, a3: 3 }; <-- stays unchanged
// ... much other code ...
const myOtherObject = modifySomehow( myObject ); <-- new version of the data
Pros, Cons, and Caveats
I couldn't give a general advice which way (copy or mutate) is "the better one".
Mutating is more performant, but can cause lots of hard-to-debug problems, if you aren't absolutely sure what's happening.
At least in somewhat complex scenarios.
1. problem with non-pure reducer
As already mentioned in my original answer, a non-pure function
might unintentionally change some outside state:
var initialValue = { a1: 1, a2: 2, a3: 3, a4: 4 };
var newKeys = [ 'n1', 'n2', 'n3' ];
var result = newKeys.reduce( (acc, key) => {
acc[key] = 'new ' + key;
return acc
}, initialValue);
console.log( 'result:', result ); // We are interested in the 'result',
console.log( 'initialValue:', initialValue ); // but the initialValue has also changed.
Somebody might argue that you can copy the initial value beforehand:
var result = newKeys.reduce( (acc, key) => {
acc[key] = 'new ' + key;
return acc
}, { ...initialValue }); // <-- copy beforehand
But this might be even less efficient in cases where e.g. the object is very big and nested,
the reducer is called often, and maybe there are multiple conditionally used small modifications
inside the reducer, which are only changing little.
(think of useReducer in React,
or the Redux reducer)
2. shallow copies
An other answer stated correctly that even with the supposedly pure approach there might still be a reference to the original object.
And this is indeed something to be aware of, but the problems arise only if you do not follow this 'immutable' approach consequently enough:
var initialValue = { a1: { value: '11'}, a2: { value: '22'} }; // <-- an object with nested 'non-primitive' values
var newObject = Object.keys(initialValue).reduce( (acc, key) => {
return {
...acc,
['newkey_' + key]: initialValue[key], // <-- copies a reference to the original object
};
}, {}); // <-- starting with empty new object, expected to be 'pure'
newObject.newkey_a1.value = 'new ref value'; // <-- changes the value of the reference
console.log( initialValue.a1 ); // <-- initialValue has changed as well
This is not a problem, if it is taken care that no references are copied (which might be not trivial sometimes):
var initialValue = { a1: { value: '11'}, a2: { value: '22'} };
var newObject = Object.keys(initialValue).reduce( (acc, key) => {
return {
...acc,
['newkey_' + key]: { value: initialValue[key].value }, // <-- copies the value
};
}, {});
newObject.newkey_a1.value = 'new ref value';
console.log( initialValue.a1 ); // <-- initialValue has not changed
3. performance
The performance is no problem with a few elements, but if the object has several thousand items, the performance becomes indeed a significant issue:
// create a large object
var myObject = {}; for( var i=0; i < 10000; i++ ){ myObject['key' + i] = i; }
// copying 10000 items takes seconds (increasing exponentially!)
// (create a new object 10000 times, with each 1,2,3,...,10000 properties)
console.time('copy')
var result = Object.keys(myObject).reduce( (acc, key)=>{
return {
...acc,
[key]: myObject[key] * 2
};
}, {});
console.timeEnd('copy');
// mutating 10000 items takes milliseconds (increasing linearly)
console.time('mutate')
var result = Object.keys(myObject).reduce( (acc, key)=>{
acc[key] = myObject[key] * 2;
return acc;
}, {});
console.timeEnd('mutate');

Typescript, turn Array of functions into merged type of all returned values

So I have a an array of functions (or actually an object of functions but it doesn't matter) which returns a different objects such as this:
const arr = [
() => ({ a: "a" }),
() => ({ b: "b" })
]
and now I want to get a type that contains all the merged values such as:
{
a: string;
b: string;
}
If tried some reduce solutions but all I've gotten to is a type that looks like:
{ a: string } | { b: string }
which isn't what I'm looking for.
Any ideas?
Update 1
The array in the example is a simplification and the actual return values of the functions are unique and is therefore needed to be kept as is => I cannot use a generalized interface such as
interface ReturnValues {
[key: string]: string;
}
Update 2
The problem is not of a JS kind but of TS and it's types. Ultimately I want to achieve this kind of functionality:
const result = arr.reduce((sum, fn) => Object.assign(sum, fn()), {})
and I want the type of result to be { a: string, b: string } so that I can call result.a and typescript will know that this is a string. If the result is { a: string } | { b: string }, calling result.a typescript says this is of the type any.
Also, for the ease of it, one can assume that there is no overlapping of the returning values of the functions.
you can use Array.reduce
const arr = [
() => ({ a: "a" }),
() => ({ b: "b" })
]
const obj = arr.reduce((acc, cur) => ({ ...acc, ...cur() }), {});
console.log(obj);
Since TypeScript doesn't have proper variadic type support yet (See this issue), the only real way to achieve what you're looking for is this:
const a = [{a:1},{b:2}] as const;
function merge<TA, TB>(a: TA, b: TB): TA & TB;
function merge<TA, TB, TC>(a: TA, b: TB, c: TC): TA & TB & TC;
function merge<TA, TB, TC, TD>(a: TA, b: TB, c: TC, d: TD): TA & TB & TC & TD;
function merge(...list: Array<any>): any {}
const b = merge(...a);
There are 3 primary methods of "mixing" javascript objects.
The process your looking to achieve is called a "mixin".
The older and more widely used method is to use whats called an extend function.
There are many ways to write an extend function, but they mostly look something like this:
const extend = (obj, mixin) => {
Object.keys(mixin).forEach(key => obj[key] = mixin[key]);
return obj;
};
here "obj" is your first object, and "mixin" is the object you want to mix into "obj", the function returns an object that is a mix of the two.
The concept here is quite simple. You loop over the keys of one object, and incrementally assign them to another, a little bit like copying a file on your hard drive.
There is a BIG DRAWBACK with this method though, and that is any properties on the destination object that have a matching name WILL get overwritten.
You can only mix two objects at a time, but you do get control over the loop at every step in case you need to do extra processing (See later on in my answer).
Newer browsers make it somewhat easier with the Object.Assign call:
Object.assign(obj1, mix1, mix2);
Here "obj1" is the final mixed object, and "mix1", "mix2" are your source objects, "obj1" will be a result of "mix1" & "mix2" being combined together.
The MDN article on "Object.Assign" can be found here:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
Like the extend function above "Object Assign" WILL overwrite properties in the destination object, but it does have the advantage of doing many at a time. My example above only shows 2 "mix" objects, but you can in theory have as many as you like, and that comes in really useful when you have them all in array as you have.
In an array you can either map the objects into one function, and then use the spread operator available in newer browsers, or you can use for..in to loop over the collection.
If your using JQuery, you can use it's foreach method, and underscore.js has dozens of ways of looping.
Since your using TypeScript you can also combine a lot of this with typescripts looping operators too.
There is a 3rd way of merging objects, it's not widely used but it is gaining traction, and that's the "Flight-Mixin" approach that uses the Array prototype, it looks something like this:
const EnumerableFirstLast = (function () { // function based module pattern.
const first = function () {
return this[0];
},
last = function () {
return this[this.length - 1];
};
return function () { // function based Flight-Mixin mechanics ...
this.first = first; // ... referring to ...
this.last = last; // ... shared code.
};
}());
EnumerableFirstLast.call(Array.prototype);
The idea here is that the two objects all ready have the functionality you require on them, so instead of "mixing" them, your just providing a single interface that delegates to them behind the scenes.
Beacuse your adding to the array prototype, you can now do things like the following:
const a = [1, 2, 3];
a.first(); // 1
a.last(); // 3
This might seem as if it's of no use, until you consider what you've in effect just done is added two new functions to a datatype you cannot normally control, this MIGHT if applied to your own objects allow you to add functions dynamically, that simply just grab the values you need to merge in a loop without too much trouble, it would however require a bit of extra planning which is why I'm adding this as more of an idea for further exploration rather than part of the solution.
This method is better suited for objects that are largely function based rather than data based as your objects seem to be.
Irrespective of which mixin method you use though, you will still need to iterate over your array collection with a loop, and you will still need to use spread to get all the keys and properties in one place.
If you consider something like
const myarr = [
{name: "peter", surname: "shaw"},
{name: "schagler", surname: "kahn"}
]
The way the spread operator works is to bust those array entries out into individual parts. So for example, IF we had the following function:
function showTwoNames(entryOne, entryTwo) {
console.log(entryOne.name + " " + entryOne.surname);
console.log(entryTwo.name + " " + entryTwo.surname);
}
You could call that function with the spread operator as follows:
showTwoNames(...myarr);
If your array had more than 2 entries in it, then the rest would be ignored in this case, the number of entries taken from the array is directly proportional to the number of arguments for the function.
You could if you wanted to do the following:
function showTwoNames(entryOne, entryTwo, ...theRest) {
console.log(entryOne.name + " " + entryOne.surname);
console.log(entryTwo.name + " " + entryTwo.surname);
console.log("There are " + theRest.length + " extra entries in the array");
}
Please NOTE that I'm not checking for nulls and undefined or anything here, it should go without saying that you should ALWAYS error check function parameters especially in JavaScript/TypeScript code.
The spread operator can in it's own right be used to combine objects, it can be simpler to understand than other methods like "ObjectAssign" beacuse quite simply you use it as follows:
var destination = { ...source1, ...source2, ...source3); // for as many sources as needed.
Like the other methods this will overwrite properties with the same name.
If you need to preserve all properties, even identically named ones, then you have no choice but to use something like an extend function, but instead of just merging directly using a for-each as my first example shows, you'll need to examine the contents of "key" while also looking in the destination to see if "key" exists and renaming as required.
Update RE: the OP's updates
So being the curious kind I am, I just tried your updated notes on one of my Linux servers, Typescript version is 3.8.3, Node is 12.14.1 and it all seems to work just as you expect it to:
I'm using all the latest versions, so it makes me wonder if your problem is maybe a bug in an old version of TS, or a feature that has only just been added in the newest build and is not present in the version your using.
Maybe try an update see what happens.
It seems that TypeScript doesn't have a native solution for this. But I found a workaround.
As mentioned in the question, using the reduce-method one gets a TS type of { a: string } | { b: string } (and to be clear, of course also a resulting object of { a: "a", b: "b" }.
However, to get from { a: string } | { b: string } to { a: string, b: string } I used the following snippet to merge the types:
type UnionToIntersection<U> = (U extends any
? (k: U) => void
: never) extends (k: infer I) => void
? I
: never;
So this would be my resulting code:
const arr = [
() => ({ a: "a" }),
() => ({ b: "b" })
]
const result = arr.reduce((sum, fn) => Object.assign(sum, fn()))
// Result is now { a: "a", b: "b" }
// but the TS type is '() => ({ a: string } | { b: string })'
type ResultUnion = ReturnType<typeof result>
// ResultUnion = { a: string } | { b: string }
type ResultIntersection = UnionToIntersection<ResultUnion>
// This is where the magic happens
// ResultIntersection = { a: string } & { b: string}
// It's not _exactly_ what I wanted, but it does the trick.
// Done

Functional Javascript - Convert to dotted format in FP way (uses Ramda)

I am learning functional programming in Javascript and using Ramda. I have this object
var fieldvalues = { name: "hello there", mobile: "1234",
meta: {status: "new"},
comments: [ {user: "john", comment: "hi"},
{user:"ram", comment: "hello"}]
};
to be converted like this:
{
comments.0.comment: "hi",
comments.0.user: "john",
comments.1.comment: "hello",
comments.1.user: "ram",
meta.status: "new",
mobile: "1234",
name: "hello there"
}
I have tried this Ramda source, which works.
var _toDotted = function(acc, obj) {
var key = obj[0], val = obj[1];
if(typeof(val) != "object") { // Matching name, mobile etc
acc[key] = val;
return acc;
}
if(!Array.isArray(val)) { // Matching meta
for(var k in val)
acc[key + "." + k] = val[k];
return acc;
}
// Matching comments
for(var idx in val) {
for(var k2 in val[idx]) {
acc[key + "." + idx + "." + k2] = val[idx][k2];
}
}
return acc;
};
// var toDotted = R.pipe(R.toPairs, R.reduce(_toDotted, {}));
var toDotted = R.pipe(R.toPairs, R.curry( function(obj) {
return R.reduce(_toDotted, {}, obj);
}));
console.log(toDotted(fieldvalues));
However, I am not sure if this is close to Functional programming methods. It just seems to be wrapped around some functional code.
Any ideas or pointers, where I can make this more functional way of writing this code.
The code snippet available here.
UPDATE 1
Updated the code to solve a problem, where the old data was getting tagged along.
Thanks
A functional approach would
use recursion to deal with arbitrarily shaped data
use multiple tiny functions as building blocks
use pattern matching on the data to choose the computation on a case-by-case basis
Whether you pass through a mutable object as an accumulator (for performance) or copy properties around (for purity) doesn't really matter, as long as the end result (on your public API) is immutable. Actually there's a nice third way that you already used: association lists (key-value pairs), which will simplify dealing with the object structure in Ramda.
const primitive = (keys, val) => [R.pair(keys.join("."), val)];
const array = (keys, arr) => R.addIndex(R.chain)((v, i) => dot(R.append(keys, i), v), arr);
const object = (keys, obj) => R.chain(([v, k]) => dot(R.append(keys, k), v), R.toPairs(obj));
const dot = (keys, val) =>
(Object(val) !== val
? primitive
: Array.isArray(val)
? array
: object
)(keys, val);
const toDotted = x => R.fromPairs(dot([], x))
Alternatively to concatenating the keys and passing them as arguments, you can also map R.prepend(key) over the result of each dot call.
Your solution is hard-coded to have inherent knowledge of the data structure (the nested for loops). A better solution would know nothing about the input data and still give you the expected result.
Either way, this is a pretty weird problem, but I was particularly bored so I figured I'd give it a shot. I mostly find this a completely pointless exercise because I cannot picture a scenario where the expected output could ever be better than the input.
This isn't a Rambda solution because there's no reason for it to be. You should understand the solution as a simple recursive procedure. If you can understand it, converting it to a sugary Rambda solution is trivial.
// determine if input is object
const isObject = x=> Object(x) === x
// flatten object
const oflatten = (data) => {
let loop = (namespace, acc, data) => {
if (Array.isArray(data))
data.forEach((v,k)=>
loop(namespace.concat([k]), acc, v))
else if (isObject(data))
Object.keys(data).forEach(k=>
loop(namespace.concat([k]), acc, data[k]))
else
Object.assign(acc, {[namespace.join('.')]: data})
return acc
}
return loop([], {}, data)
}
// example data
var fieldvalues = {
name: "hello there",
mobile: "1234",
meta: {status: "new"},
comments: [
{user: "john", comment: "hi"},
{user: "ram", comment: "hello"}
]
}
// show me the money ...
console.log(oflatten(fieldvalues))
Total function
oflatten is reasonably robust and will work on any input. Even when the input is an array, a primitive value, or undefined. You can be certain you will always get an object as output.
// array input example
console.log(oflatten(['a', 'b', 'c']))
// {
// "0": "a",
// "1": "b",
// "2": "c"
// }
// primitive value example
console.log(oflatten(5))
// {
// "": 5
// }
// undefined example
console.log(oflatten())
// {
// "": undefined
// }
How it works …
It takes an input of any kind, then …
It starts the loop with two state variables: namespace and acc . acc is your return value and is always initialized with an empty object {}. And namespace keeps track of the nesting keys and is always initialized with an empty array, []
notice I don't use a String to namespace the key because a root namespace of '' prepended to any key will always be .somekey. That is not the case when you use a root namespace of [].
Using the same example, [].concat(['somekey']).join('.') will give you the proper key, 'somekey'.
Similarly, ['meta'].concat(['status']).join('.') will give you 'meta.status'. See? Using an array for the key computation will make this a lot easier.
The loop has a third parameter, data, the current value we are processing. The first loop iteration will always be the original input
We do a simple case analysis on data's type. This is necessary because JavaScript doesn't have pattern matching. Just because were using a if/else doesn't mean it's not functional paradigm.
If data is an Array, we want to iterate through the array, and recursively call loop on each of the child values. We pass along the value's key as namespace.concat([k]) which will become the new namespace for the nested call. Notice, that nothing gets assigned to acc at this point. We only want to assign to acc when we have reached a value and until then, we're just building up the namespace.
If the data is an Object, we iterate through it just like we did with an Array. There's a separate case analysis for this because the looping syntax for objects is slightly different than arrays. Otherwise, it's doing the exact same thing.
If the data is neither an Array or an Object, we've reached a value. At this point we can assign the data value to the acc using the built up namespace as the key. Because we're done building the namespace for this key, all we have to do compute the final key is namespace.join('.') and everything works out.
The resulting object will always have as many pairs as values that were found in the original object.

Define a custom hash() method for use with ES6 maps

To illustrate the problem, consider the following simple object
function Key( val ) {
this._val = val;
}
Now I create a ES6 Map instance and feed one entry into it like this
var map = new Map(),
key1 = new Key( 'key' );
map.set( key1, 'some value' );
console.log( 'key1: ', map.has( key1 ) );
// key1: true
So far everything is fine. The challenge, however, comes up, if I create a nearly identical object key2 like this
var key2 = new Key( 'key' );
So basically both keys are identical, but obviously key2 is not part of the map
console.log( 'key2: ', map.has( key2 ) );
// key2: false
JavaScript uses the object references as a key here, so the two separate objects will not point towards the same value.
What I would like to do now is, to add something like a hash() method to key's prototype, so that both object would point to the same key. Is something like this possible?
I know, that there would be a way to circumvent the problem using a factory pattern for the Key generation together with some caching. However, this results in a lot of problem regarding immutability of the objects and the cache preventing old objects from being garbage collected. So I think that is not really an option.
Is something like this possible?
No, this is a known flaw of ES6 Collections. All they do is check for reference identity, and there is no way to change that.
The best thing you can do (if hash consing the instances is not an option as you say) is not to use objects for the keys. Instead, use strings that encode the Key values, and convert back and forth between the two representations. Given that you consider your keys to be immutable, this should not pose a problem.
I've created a class called CanonMap in my library big-m to encapsulate mapping by hash instead of reference.
By default, it works with tuples, Dates, and simple objects:
const { CanonMap } = "big-m";
const myMap = new CanonMap();
myMap.set(
["Farooq", "867-5309"],
36.59
);
myMap.get(
["Farooq", "867-5309"]
) === 36.59;
myMap.set(
{name: "Farooq", number: "867-5309"},
36.59
);
myMap.get(
{number: "867-5309", name: "Farooq"} // Insensitive to key ordering
) === 36.59;
myMap.set(new Date(2012, 6, 5), "Tuesday");
myMap.get(new Date(2012, 6, 5)) === "Tuesday";
It can also be extended with a custom "canonizer" function that determines how to hash values:
import {naiveCanonize, jsonCanonize, JsonCanonMap, CanonMap} from "big-m";
// Same as default canonizer, but with greater recursion depth (default is 2)
new CanonMap([], 6);
// Canonize by ID with fallback to naive
const customCanonMap = new CanonMap([
[{id: "TEST1", x: 7}, 77],
[{ x: 7 }, 88]
], lookup => lookup.id || naiveCanonize(lookup));
customCanonMap.get({id: "TEST1", x: 8}) === 77; // Ignores other values, uses ID
customCanonMap.get({x: 8}) === undefined; // Uses all fields, so lookup fails
// Default canonizer with JSON.stringify
new CanonMap([], jsonCanonize);
// equivalent to
new CanonMap([], lookup => JSON.stringify(lookup));
// also equivalent to
new JsonCanonMap();
Finally, to implement a CanonMap that makes use of a prototype hash function on the object itself, as you described, you could do something like this:
const selfHashingCanonMap = new CanonMap([], lookup => {
if ("hash" in lookup) {
return lookup.hash();
} else {
return naiveCanonize(lookup);
}
});

How do I remove a property from a JavaScript object?

Given an object:
let myObject = {
"ircEvent": "PRIVMSG",
"method": "newURI",
"regex": "^http://.*"
};
How do I remove the property regex to end up with the following myObject?
let myObject = {
"ircEvent": "PRIVMSG",
"method": "newURI"
};
To remove a property from an object (mutating the object), you can do it like this:
delete myObject.regex;
// or,
delete myObject['regex'];
// or,
var prop = "regex";
delete myObject[prop];
Demo
var myObject = {
"ircEvent": "PRIVMSG",
"method": "newURI",
"regex": "^http://.*"
};
delete myObject.regex;
console.log(myObject);
For anyone interested in reading more about it, Stack Overflow user kangax has written an incredibly in-depth blog post about the delete statement on their blog, Understanding delete. It is highly recommended.
If you'd like a new object with all the keys of the original except some, you could use destructuring.
Demo
let myObject = {
"ircEvent": "PRIVMSG",
"method": "newURI",
"regex": "^http://.*"
};
// assign the key regex to the variable _ indicating it will be unused
const {regex: _, ...newObj} = myObject;
console.log(newObj); // has no 'regex' key
console.log(myObject); // remains unchanged
Objects in JavaScript can be thought of as maps between keys and values. The delete operator is used to remove these keys, more commonly known as object properties, one at a time.
var obj = {
myProperty: 1
}
console.log(obj.hasOwnProperty('myProperty')) // true
delete obj.myProperty
console.log(obj.hasOwnProperty('myProperty')) // false
The delete operator does not directly free memory, and it differs from simply assigning the value of null or undefined to a property, in that the property itself is removed from the object. Note that if the value of a deleted property was a reference type (an object), and another part of your program still holds a reference to that object, then that object will, of course, not be garbage collected until all references to it have disappeared.
delete will only work on properties whose descriptor marks them as configurable.
Old question, modern answer. Using object destructuring, an ECMAScript 6 feature, it's as simple as:
const { a, ...rest } = { a: 1, b: 2, c: 3 };
Or with the questions sample:
const myObject = {"ircEvent": "PRIVMSG", "method": "newURI", "regex": "^http://.*"};
const { regex, ...newObject } = myObject;
console.log(newObject);
You can see it in action in the Babel try-out editor.
Edit:
To reassign to the same variable, use a let:
let myObject = {"ircEvent": "PRIVMSG", "method": "newURI", "regex": "^http://.*"};
({ regex, ...myObject } = myObject);
console.log(myObject);
var myObject = {"ircEvent": "PRIVMSG", "method": "newURI", "regex": "^http://.*"};
delete myObject.regex;
console.log ( myObject.regex); // logs: undefined
This works in Firefox and Internet Explorer, and I think it works in all others.
The delete operator is used to remove properties from objects.
const obj = { foo: "bar" };
delete obj.foo;
obj.hasOwnProperty("foo"); // false
Note that, for arrays, this is not the same as removing an element. To remove an element from an array, use Array#splice or Array#pop. For example:
arr; // [0, 1, 2, 3, 4]
arr.splice(3,1); // 3
arr; // [0, 1, 2, 4]
Details
Strictly speaking, it's impossible to truly delete anything in JavaScript. The delete operator neither deletes objects nor frees memory. Rather, it sets its operand to undefined and manipulates the parent object so that the member is gone.
let parent = {
member: { str: "Hello" }
};
let secondref = parent.member;
delete parent.member;
parent.member; // undefined
secondref; // { str: "Hello" }
The object is not deleted. Only the reference is. Memory is only freed
by the garbage collector when all references to an object are removed.
Another important caveat is that the delete operator will not reorganize structures for you, which has results that can seem counterintuitive. Deleting an array index, for example, will leave a "hole" in it.
let array = [0, 1, 2, 3]; // [0, 1, 2, 3]
delete array[2]; // [0, 1, empty, 3]
This is because arrays are objects. So indices are the same as keys.
let fauxarray = {0: 1, 1: 2, length: 2};
fauxarray.__proto__ = [].__proto__;
fauxarray.push(3);
fauxarray; // [1, 2, 3]
Array.isArray(fauxarray); // false
Array.isArray([1, 2, 3]); // true
Different built-in functions in JavaScript handle arrays with holes in them differently.
for..in statements will skip the empty index completely.
A naive for loop will yield undefined for the value at the index.
Any method using Symbol.iterator will return undefined for the value at the index.
forEach, map and reduce will simply skip the missing index, but will not remove it
Example:
let array = [1, 2, 3]; // [1,2,3]
delete array[1]; // [1, empty, 3]
array.map(x => 0); // [0, empty, 0]
So, the delete operator should not be used for the common use-case of removing elements from an array. Arrays have a dedicated methods for removing elements and reallocating memory: Array#splice() and Array#pop.
Array#splice(start[, deleteCount[, item1[, item2[, ...]]]])
Array#splice mutates the array, and returns any removed indices. deleteCount elements are removed from index start, and item1, item2... itemN are inserted into the array from index start. If deleteCount is omitted then elements from startIndex are removed to the end of the array.
let a = [0,1,2,3,4]
a.splice(2,2) // returns the removed elements [2,3]
// ...and `a` is now [0,1,4]
There is also a similarly named, but different, function on Array.prototype: Array#slice.
Array#slice([begin[, end]])
Array#slice is non-destructive, and returns a new array containing the indicated indices from start to end. If end is left unspecified, it defaults to the end of the array. If end is positive, it specifies the zero-based non-inclusive index to stop at. If end is negative it, it specifies the index to stop at by counting back from the end of the array (eg. -1 will omit the final index). If end <= start, the result is an empty array.
let a = [0,1,2,3,4]
let slices = [
a.slice(0,2),
a.slice(2,2),
a.slice(2,3),
a.slice(2,5) ]
// a [0,1,2,3,4]
// slices[0] [0 1]- - -
// slices[1] - - - - -
// slices[2] - -[3]- -
// slices[3] - -[2 4 5]
Array#pop
Array#pop removes the last element from an array, and returns that element. This operation changes the length of the array. The opposite operation is push
Array#shift
Array#shift is similar to pop, except it removes the first element. The opposite operation is unshift.
Spread Syntax (ES6)
To complete Koen's answer, in case you want to remove a dynamic variable using the spread syntax, you can do it like so:
const key = 'a';
const { [key]: foo, ...rest } = { a: 1, b: 2, c: 3 };
console.log(foo); // 1
console.log(rest); // { b: 2, c: 3 }
* foo will be a new variable with the value of a (which is 1).
Extended answer 😇
There are a few common ways to remove a property from an object. Each one has its own pros and cons (check this performance comparison):
Delete Operator
It is readable and short, however, it might not be the best choice if you are operating on a large number of objects as its performance is not optimized.
delete obj[key];
Reassignment
It is more than two times faster than delete, however the property is not deleted and can be iterated.
obj[key] = null;
obj[key] = false;
obj[key] = undefined;
Spread Operator
This ES6 operator allows us to return a brand new object, excluding any properties, without mutating the existing object. The downside is that it has the worse performance out of the above and is not suggested to be used when you need to remove many properties at a time.
{ [key]: val, ...rest } = obj;
Another alternative is to use the Underscore.js library.
Note that _.pick() and _.omit() both return a copy of the object and don't directly modify the original object. Assigning the result to the original object should do the trick (not shown).
Reference: link _.pick(object, *keys)
Return a copy of the object, filtered to only have values for the
whitelisted keys (or array of valid keys).
var myJSONObject =
{"ircEvent": "PRIVMSG", "method": "newURI", "regex": "^http://.*"};
_.pick(myJSONObject, "ircEvent", "method");
=> {"ircEvent": "PRIVMSG", "method": "newURI"};
Reference: link _.omit(object, *keys)
Return a copy of the object, filtered to omit the
blacklisted keys (or array of keys).
var myJSONObject =
{"ircEvent": "PRIVMSG", "method": "newURI", "regex": "^http://.*"};
_.omit(myJSONObject, "regex");
=> {"ircEvent": "PRIVMSG", "method": "newURI"};
For arrays, _.filter() and _.reject() can be used in a similar manner.
To clone an object without a property:
For example:
let object = { a: 1, b: 2, c: 3 };
And we need to delete a.
With an explicit prop key:
const { a, ...rest } = object;
object = rest;
With a variable prop key:
const propKey = 'a';
const { [propKey]: propValue, ...rest } = object;
object = rest;
A cool arrow function 😎:
const removeProperty = (propKey, { [propKey]: propValue, ...rest }) => rest;
object = removeProperty('a', object);
For multiple properties
const removeProperties = (object, ...keys) => (keys.length ? removeProperties(removeProperty(keys.pop(), object), ...keys) : object);
Usage
object = removeProperties(object, 'a', 'b') // result => { c: 3 }
Or
const propsToRemove = ['a', 'b']
object = removeProperties(object, ...propsToRemove) // result => { c: 3 }
The term you have used in your question title, Remove a property from a JavaScript object, can be interpreted in some different ways. The one is to remove it for whole the memory and the list of object keys or the other is just to remove it from your object. As it has been mentioned in some other answers, the delete keyword is the main part. Let's say you have your object like:
myJSONObject = {"ircEvent": "PRIVMSG", "method": "newURI", "regex": "^http://.*"};
If you do:
console.log(Object.keys(myJSONObject));
the result would be:
["ircEvent", "method", "regex"]
You can delete that specific key from your object keys like:
delete myJSONObject["regex"];
Then your objects key using Object.keys(myJSONObject) would be:
["ircEvent", "method"]
But the point is if you care about memory and you want to whole the object gets removed from the memory, it is recommended to set it to null before you delete the key:
myJSONObject["regex"] = null;
delete myJSONObject["regex"];
The other important point here is to be careful about your other references to the same object. For instance, if you create a variable like:
var regex = myJSONObject["regex"];
Or add it as a new pointer to another object like:
var myOtherObject = {};
myOtherObject["regex"] = myJSONObject["regex"];
Then even if you remove it from your object myJSONObject, that specific object won't get deleted from the memory, since the regex variable and myOtherObject["regex"] still have their values. Then how could we remove the object from the memory for sure?
The answer would be to delete all the references you have in your code, pointed to that very object and also not use var statements to create new references to that object. This last point regarding var statements, is one of the most crucial issues that we are usually faced with, because using var statements would prevent the created object from getting removed.
Which means in this case you won't be able to remove that object because you have created the regex variable via a var statement, and if you do:
delete regex; //False
The result would be false, which means that your delete statement haven't been executed as you expected. But if you had not created that variable before, and you only had myOtherObject["regex"] as your last existing reference, you could have done this just by removing it like:
myOtherObject["regex"] = null;
delete myOtherObject["regex"];
In other words, a JavaScript object is eligible to be killed as soon as there is no reference left in your code pointed to that object.
Update:
Thanks to #AgentME:
Setting a property to null before deleting it doesn't accomplish
anything (unless the object has been sealed by Object.seal and the
delete fails. That's not usually the case unless you specifically
try).
To get more information on Object.seal: Object.seal()
ECMAScript 2015 (or ES6) came with built-in Reflect object. It is possible to delete object property by calling Reflect.deleteProperty() function with target object and property key as parameters:
Reflect.deleteProperty(myJSONObject, 'regex');
which is equivalent to:
delete myJSONObject['regex'];
But if the property of the object is not configurable it cannot be deleted neither with deleteProperty function nor delete operator:
let obj = Object.freeze({ prop: "value" });
let success = Reflect.deleteProperty(obj, "prop");
console.log(success); // false
console.log(obj.prop); // value
Object.freeze() makes all properties of object not configurable (besides other things). deleteProperty function (as well as delete operator) returns false when tries to delete any of it's properties. If property is configurable it returns true, even if property does not exist.
The difference between delete and deleteProperty is when using strict mode:
"use strict";
let obj = Object.freeze({ prop: "value" });
Reflect.deleteProperty(obj, "prop"); // false
delete obj["prop"];
// TypeError: property "prop" is non-configurable and can't be deleted
Suppose you have an object that looks like this:
var Hogwarts = {
staff : [
'Argus Filch',
'Filius Flitwick',
'Gilderoy Lockhart',
'Minerva McGonagall',
'Poppy Pomfrey',
...
],
students : [
'Hannah Abbott',
'Katie Bell',
'Susan Bones',
'Terry Boot',
'Lavender Brown',
...
]
};
Deleting an object property
If you want to use the entire staff array, the proper way to do this, would be to do this:
delete Hogwarts.staff;
Alternatively, you could also do this:
delete Hogwarts['staff'];
Similarly, removing the entire students array would be done by calling delete Hogwarts.students; or delete Hogwarts['students'];.
Deleting an array index
Now, if you want to remove a single staff member or student, the procedure is a bit different, because both properties are arrays themselves.
If you know the index of your staff member, you could simply do this:
Hogwarts.staff.splice(3, 1);
If you do not know the index, you'll also have to do an index search:
Hogwarts.staff.splice(Hogwarts.staff.indexOf('Minerva McGonnagall') - 1, 1);
Note
While you technically can use delete for an array, using it would result in getting incorrect results when calling for example Hogwarts.staff.length later on. In other words, delete would remove the element, but it wouldn't update the value of length property. Using delete would also mess up your indexing.
So, when deleting values from an object, always first consider whether you're dealing with object properties or whether you're dealing with array values, and choose the appropriate strategy based on that.
If you want to experiment with this, you can use this Fiddle as a starting point.
I personally use Underscore.js or Lodash for object and array manipulation:
myObject = _.omit(myObject, 'regex');
Using delete method is the best way to do that, as per MDN description, the delete operator removes a property from an object. So you can simply write:
delete myObject.regex;
// OR
delete myObject['regex'];
The delete operator removes a given property from an object. On
successful deletion, it will return true, else false will be returned.
However, it is important to consider the following scenarios:
If the property which you are trying to delete does not exist, delete
will not have any effect and will return true
If a property with the same name exists on the object's prototype
chain, then, after deletion, the object will use the property from the
prototype chain (in other words, delete only has an effect on own
properties).
Any property declared with var cannot be deleted from the global scope
or from a function's scope.
As such, delete cannot delete any functions in the global scope (whether this is part of a function definition or a function (expression).
Functions which are part of an object (apart from the
global scope) can be deleted with delete.
Any property declared with let or const cannot be deleted from the scope within which they were defined. Non-configurable properties cannot be removed. This includes properties of built-in objects like Math, Array, Object and properties that are created as non-configurable with methods like Object.defineProperty().
The following snippet gives another simple example:
var Employee = {
age: 28,
name: 'Alireza',
designation: 'developer'
}
console.log(delete Employee.name); // returns true
console.log(delete Employee.age); // returns true
// When trying to delete a property that does
// not exist, true is returned
console.log(delete Employee.salary); // returns true
For more info about and seeing more examples visit the link below:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete
Another solution, using Array#reduce.
var myObject = {
"ircEvent": "PRIVMSG",
"method": "newURI",
"regex": "^http://.*"
};
myObject = Object.keys(myObject).reduce(function(obj, key) {
if (key != "regex") { //key you want to remove
obj[key] = myObject[key];
}
return obj;
}, {});
console.log(myObject);
However, it will mutate the original object. If you want to create a new object without the specified key, just assign the reduce function to a new variable, e.g.:
(ES6)
const myObject = {
ircEvent: 'PRIVMSG',
method: 'newURI',
regex: '^http://.*',
};
const myNewObject = Object.keys(myObject).reduce((obj, key) => {
key !== 'regex' ? obj[key] = myObject[key] : null;
return obj;
}, {});
console.log(myNewObject);
There are a lot of good answers here but I just want to chime in that when using delete to remove a property in JavaScript, it is often wise to first check if that property exists to prevent errors.
E.g
var obj = {"property":"value", "property2":"value"};
if (obj && obj.hasOwnProperty("property2")) {
delete obj.property2;
} else {
//error handling
}
Due to the dynamic nature of JavaScript there are often cases where you simply don't know if the property exists or not. Checking if obj exists before the && also makes sure you don't throw an error due to calling the hasOwnProperty() function on an undefined object.
Sorry if this didn't add to your specific use case but I believe this to be a good design to adapt when managing objects and their properties.
This post is very old and I find it very helpful so I decided to share the unset function I wrote in case someone else see this post and think why it's not so simple as it in PHP unset function.
The reason for writing this new unset function, is to keep the index of all other variables in this hash_map. Look at the following example, and see how the index of "test2" did not change after removing a value from the hash_map.
function unset(unsetKey, unsetArr, resort) {
var tempArr = unsetArr;
var unsetArr = {};
delete tempArr[unsetKey];
if (resort) {
j = -1;
}
for (i in tempArr) {
if (typeof(tempArr[i]) !== 'undefined') {
if (resort) {
j++;
} else {
j = i;
}
unsetArr[j] = tempArr[i];
}
}
return unsetArr;
}
var unsetArr = ['test', 'deletedString', 'test2'];
console.log(unset('1', unsetArr, true)); // output Object {0: "test", 1: "test2"}
console.log(unset('1', unsetArr, false)); // output Object {0: "test", 2: "test2"}
There are a couple of ways to remove properties from an object:
1) Remove using a dot property accessor (mutable)
const myObject = {
"ircEvent": "PRIVMSG",
"method": "newURI",
"regex": "^http://.*",
};
delete myObject.regex;
console.log(myObject);
2. Remove using square brackets property accessor (mutable)
const myObject = {
"ircEvent": "PRIVMSG",
"method": "newURI",
"regex": "^http://.*",
};
delete myObject['regex'];
console.log(myObject);
// or
const name = 'ircEvent';
delete myObject[name];
console.log(myObject);
3) Alternative option but without altering the original object, is using object destructuring and rest syntax (immutable)
const myObject = {
"ircEvent": "PRIVMSG",
"method": "newURI",
"regex": "^http://.*",
};
const { regex, ...myObjectRest} = myObject;
console.log(myObjectRest);
Using ramda#dissoc you will get a new object without the attribute regex:
const newObject = R.dissoc('regex', myObject);
// newObject !== myObject
You can also use other functions to achieve the same effect - omit, pick, ...
Try the following method. Assign the Object property value to undefined. Then stringify the object and parse.
var myObject = {"ircEvent": "PRIVMSG", "method": "newURI", "regex": "^http://.*"};
myObject.regex = undefined;
myObject = JSON.parse(JSON.stringify(myObject));
console.log(myObject);
Using Lodash
import omit from 'lodash/omit';
const prevObject = {test: false, test2: true};
// Removes test2 key from previous object
const nextObject = omit(prevObject, 'test2');
Using Ramda
R.omit(['a', 'd'], {a: 1, b: 2, c: 3, d: 4}); //=> {b: 2, c: 3}
If you want to delete a property deeply nested in the object then you can use the following recursive function with path to the property as the second argument:
var deepObjectRemove = function(obj, path_to_key){
if(path_to_key.length === 1){
delete obj[path_to_key[0]];
return true;
}else{
if(obj[path_to_key[0]])
return deepObjectRemove(obj[path_to_key[0]], path_to_key.slice(1));
else
return false;
}
};
Example:
var a = {
level1:{
level2:{
level3: {
level4: "yolo"
}
}
}
};
deepObjectRemove(a, ["level1", "level2", "level3"]);
console.log(a);
//Prints {level1: {level2: {}}}
Object.assign() & Object.keys() & Array.map()
const obj = {
"Filters":[
{
"FilterType":"between",
"Field":"BasicInformationRow.A0",
"MaxValue":"2017-10-01",
"MinValue":"2017-09-01",
"Value":"Filters value"
}
]
};
let new_obj1 = Object.assign({}, obj.Filters[0]);
let new_obj2 = Object.assign({}, obj.Filters[0]);
/*
// old version
let shaped_obj1 = Object.keys(new_obj1).map(
(key, index) => {
switch (key) {
case "MaxValue":
delete new_obj1["MaxValue"];
break;
case "MinValue":
delete new_obj1["MinValue"];
break;
}
return new_obj1;
}
)[0];
let shaped_obj2 = Object.keys(new_obj2).map(
(key, index) => {
if(key === "Value"){
delete new_obj2["Value"];
}
return new_obj2;
}
)[0];
*/
// new version!
let shaped_obj1 = Object.keys(new_obj1).forEach(
(key, index) => {
switch (key) {
case "MaxValue":
delete new_obj1["MaxValue"];
break;
case "MinValue":
delete new_obj1["MinValue"];
break;
default:
break;
}
}
);
let shaped_obj2 = Object.keys(new_obj2).forEach(
(key, index) => {
if(key === "Value"){
delete new_obj2["Value"];
}
}
);
Dan's assertion that 'delete' is very slow and the benchmark he posted were doubted. So I carried out the test myself in Chrome 59. It does seem that 'delete' is about 30 times slower:
var iterationsTotal = 10000000; // 10 million
var o;
var t1 = Date.now(),t2;
for (let i=0; i<iterationsTotal; i++) {
o = {a:1,b:2,c:3,d:4,e:5};
delete o.a; delete o.b; delete o.c; delete o.d; delete o.e;
}
console.log ((t2=Date.now())-t1); // 6135
for (let i=0; i<iterationsTotal; i++) {
o = {a:1,b:2,c:3,d:4,e:5};
o.a = o.b = o.c = o.d = o.e = undefined;
}
console.log (Date.now()-t2); // 205
Note that I purposely carried out more than one 'delete' operations in one loop cycle to minimize the effect caused by the other operations.
Property Removal in JavaScript
There are many different options presented on this page, not because most of the options are wrong—or because the answers are duplicates—but because the appropriate technique depends on the situation you're in and the goals of the tasks you and/or you team are trying to fulfill. To answer you question unequivocally, one needs to know:
The version of ECMAScript you're targeting
The range of object types you want to remove properties on and the type of property names you need to be able to omit (Strings only? Symbols? Weak references mapped from arbitrary objects? These have all been types of property pointers in JavaScript for years now)
The programming ethos/patterns you and your team use. Do you favor functional approaches and mutation is verboten on your team, or do you employ wild west mutative object-oriented techniques?
Are you looking to achieve this in pure JavaScript or are you willing & able to use a 3rd-party library?
Once those four queries have been answered, there are essentially four categories of "property removal" in JavaScript to chose from in order to meet your goals. They are:
Mutative object property deletion, unsafe
This category is for operating on object literals or object instances when you want to retain/continue to use the original reference and aren't using stateless functional principles in your code. An example piece of syntax in this category:
'use strict'
const iLikeMutatingStuffDontI = { myNameIs: 'KIDDDDD!', [Symbol.for('amICool')]: true }
delete iLikeMutatingStuffDontI[Symbol.for('amICool')] // true
Object.defineProperty({ myNameIs: 'KIDDDDD!', 'amICool', { value: true, configurable: false })
delete iLikeMutatingStuffDontI['amICool'] // throws
This category is the oldest, most straightforward & most widely supported category of property removal. It supports Symbol & array indexes in addition to strings and works in every version of JavaScript except for the very first release. However, it's mutative which violates some programming principles and has performance implications. It also can result in uncaught exceptions when used on non-configurable properties in strict mode.
Rest-based string property omission
This category is for operating on plain object or array instances in newer ECMAScript flavors when a non-mutative approach is desired and you don't need to account for Symbol keys:
const foo = { name: 'KIDDDDD!', [Symbol.for('isCool')]: true }
const { name, ...coolio } = foo // coolio doesn't have "name"
const { isCool, ...coolio2 } = foo // coolio2 has everything from `foo` because `isCool` doesn't account for Symbols :(
Mutative object property deletion, safe
This category is for operating on object literals or object instances when you want to retain/continue to use the original reference while guarding against exceptions being thrown on unconfigurable properties:
'use strict'
const iLikeMutatingStuffDontI = { myNameIs: 'KIDDDDD!', [Symbol.for('amICool')]: true }
Reflect.deleteProperty(iLikeMutatingStuffDontI, Symbol.for('amICool')) // true
Object.defineProperty({ myNameIs: 'KIDDDDD!', 'amICool', { value: true, configurable: false })
Reflect.deleteProperty(iLikeMutatingStuffDontI, 'amICool') // false
In addition, while mutating objects in-place isn't stateless, you can use the functional nature of Reflect.deleteProperty to do partial application and other functional techniques that aren't possible with delete statements.
Syntax-based string property omission
This category is for operating on plain object or array instances in newer ECMAScript flavors when a non-mutative approach is desired and you don't need to account for Symbol keys:
const foo = { name: 'KIDDDDD!', [Symbol.for('isCool')]: true }
const { name, ...coolio } = foo // coolio doesn't have "name"
const { isCool, ...coolio2 } = foo // coolio2 has everything from `foo` because `isCool` doesn't account for Symbols :(
Library-based property omission
This category is generally allows for greater functional flexibility, including accounting for Symbols & omitting more than one property in one statement:
const o = require("lodash.omit")
const foo = { [Symbol.for('a')]: 'abc', b: 'b', c: 'c' }
const bar = o(foo, 'a') // "'a' undefined"
const baz = o(foo, [ Symbol.for('a'), 'b' ]) // Symbol supported, more than one prop at a time, "Symbol.for('a') undefined"
Here's an ES6 way to remove the entry easily:
let myObject = {
"ircEvent": "PRIVMSG",
"method": "newURI",
"regex": "^http://.*"
};
const removeItem = 'regex';
const { [removeItem]: remove, ...rest } = myObject;
console.log(remove); // "^http://.*"
console.log(rest); // Object { ircEvent: "PRIVMSG", method: "newURI" }
#johnstock, we can also use JavaScript's prototyping concept to add method to objects to delete any passed key available in calling object.
Above answers are appreciated.
var myObject = {
"ircEvent": "PRIVMSG",
"method": "newURI",
"regex": "^http://.*"
};
// 1st and direct way
delete myObject.regex; // delete myObject["regex"]
console.log(myObject); // { ircEvent: 'PRIVMSG', method: 'newURI' }
// 2 way - by using the concept of JavaScript's prototyping concept
Object.prototype.removeFromObjectByKey = function(key) {
// If key exists, remove it and return true
if (this[key] !== undefined) {
delete this[key]
return true;
}
// Else return false
return false;
}
var isRemoved = myObject.removeFromObjectByKey('method')
console.log(myObject) // { ircEvent: 'PRIVMSG' }
// More examples
var obj = {
a: 45,
b: 56,
c: 67
}
console.log(obj) // { a: 45, b: 56, c: 67 }
// Remove key 'a' from obj
isRemoved = obj.removeFromObjectByKey('a')
console.log(isRemoved); //true
console.log(obj); // { b: 56, c: 67 }
// Remove key 'd' from obj which doesn't exist
var isRemoved = obj.removeFromObjectByKey('d')
console.log(isRemoved); // false
console.log(obj); // { b: 56, c: 67 }
You can use a filter like below
var myObject = {
"ircEvent": "PRIVMSG",
"method": "newURI",
"regex": "^http://.*"
};
// Way 1
let filter1 = {}
Object.keys({...myObject}).filter(d => {
if(d !== 'regex'){
filter1[d] = myObject[d];
}
})
console.log(filter1)
// Way 2
let filter2 = Object.fromEntries(Object.entries({...myObject}).filter(d =>
d[0] !== 'regex'
))
console.log(filter2)
I have used Lodash "unset" to make it happen for a nested object also... only this needs to write small logic to get the path of the property key which is expected by the omit method.
Method which returns the property path as an array
var a = {"bool":{"must":[{"range":{"price_index.final_price":{"gt":"450", "lt":"500"}}}, {"bool":{"should":[{"term":{"color_value.keyword":"Black"}}]}}]}};
function getPathOfKey(object,key,currentPath, t){
var currentPath = currentPath || [];
for(var i in object){
if(i == key){
t = currentPath;
}
else if(typeof object[i] == "object"){
currentPath.push(i)
return getPathOfKey(object[i], key,currentPath)
}
}
t.push(key);
return t;
}
document.getElementById("output").innerHTML =JSON.stringify(getPathOfKey(a,"price_index.final_price"))
<div id="output">
</div>
Then just using Lodash unset method remove property from object.
var unset = require('lodash.unset');
unset(a, getPathOfKey(a, "price_index.final_price"));
let myObject = {
"ircEvent": "PRIVMSG",
"method": "newURI",
"regex": "^http://.*"
};
obj = Object.fromEntries(
Object.entries(myObject).filter(function (m){
return m[0] != "regex"/*or whatever key to delete*/
}
))
console.log(obj)
You can also just treat the object like a2d array using Object.entries, and use splice to remove an element as you would in a normal array, or simply filter through the object, as one would an array, and assign the reconstructed object back to the original variable
If you don't want to modify the original object.
Remove a property without mutating the object
If mutability is a concern, you can create a completely new object by copying all the properties from the old, except the one you want to remove.
let myObject = {
"ircEvent": "PRIVMSG",
"method": "newURI",
"regex": "^http://.*"
};
let prop = 'regex';
const updatedObject = Object.keys(myObject).reduce((object, key) => {
if (key !== prop) {
object[key] = myObject[key]
}
return object
}, {})
console.log(updatedObject);

Categories

Resources