Merge array into object properties in JavaScript - javascript

I have a trivial issue where I have two simple objects like this
var state = {
Value1: "Something",
Value2: "Else"
};
var claims = [
"Viewing",
"Editing",
"Delete"
];
and I would like to create an object that looks like this.
var newState = {
Value1: "Something",
Value2: "Else",
/* Array merged into properties with defaults. */
Viewing: true,
Editing: true,
Delete: true
};
I considered using jQuery $.map() and alternatively Object.assign() (which seems to be closer to what I want) but I cant seem to work out exactly how to achieve the result I need.
So as the heading states, how do I merge an array into an existing object as properties while also setting default values?

For the simple given example try the following:
var state = {Value1:"Something",Value2:"Else"};
claims = ["Viewing","Editing","Delete"];
let res = Object.assign(state, claims.reduce((a,c) => {a[c] = true; return a},{}))
console.log(res)

You can add a property using variable name with obj[myStringAsName] = value
var state = {
Value1: "Something",
Value2: "Else"
};
var claims = [
"Viewing",
"Editing",
"Delete"
];
var newState = state;
claims.forEach(function(item){
newState[item] = true;
})
console.log(newState)

I converted the array to object using reduce and merged both objects
var state = {
Value1: "Something",
Value2: "Else"
};
var claims = [
"Viewing",
"Editing",
"Delete"
];
var obj = claims.reduce(function(acc, cur) {
acc[cur] = true;
return acc;
}, {});
var returnedData = Object.assign(state, obj);
console.log(returnedData)

You could use destructuring and a simple Array#forEach
const state = {
Value1: "Something",
Value2: "Else"
};
const claims = [
"Viewing",
"Editing",
"Delete"
];
const newState = {
...state
}
claims.forEach(prop => newState[prop] = true);
console.log(newState)

Related

Javascript conditionally adding the nested object using map() operator

I have a below JSON,
var original = {
"todos": [
{
"accountNo": "50190000",
"name": "Sarkar",
"vpainfo": [
{
"vpa": "log#bda",
"mccCode": "0000"
}
]
}
]
}
And am trying to add new data inside the nested array i.e., "vpainfo". I have tried using the below code and able to adding the new values inside "vpainfo".
var newdata = {"vpa":"first#bda","mccCode":"1111"};
var newObj =
Object.assign({}, original,
{
todos: original.todos.map(todoInfo=>(todoInfo.accountNo=="50190000")?[
...todoInfo.vpainfo,
newdata
]: todoInfo)
});
And the resulted object is,
{"todos":[[{"vpa":"log#bda","mccCode":"0000"},{"vpa":"first#bda","mccCode":"1111"}]]}
But few of the key and values(accountNo and name) are getting missed, how do we get the full object with the latest updated values?
You only return the array, not the actual object, hence the error.
var original = {
"todos": [
{
"accountNo": "50190000",
"name": "Sarkar",
"vpainfo": [
{
"vpa": "log#bda",
"mccCode": "0000"
}
]
}
]
}
const newdata = {"vpa":"first#bda","mccCode":"1111"};
const newObj = Object.assign({}, original,
{
todos: original.todos.map(todoInfo=>{
if(todoInfo.accountNo=="50190000"){
return {
...todoInfo,
vpainfo: [...todoInfo.vpainfo, newdata]
}
}
return todoInfo
})
});
console.log(newObj)
All those spread operators seem a little excessive...
If all you wanna do is add newdata to that existing array, then do that:
var original = {
"todos": [{
"accountNo": "50190000",
"name": "Sarkar",
"vpainfo": [{
"vpa": "log#bda",
"mccCode": "0000"
}]
}]
};
const newdata = {
"vpa": "first#bda",
"mccCode": "1111"
};
// Find the correct account.
const account = original.todos.filter(t => t.accountNo === '50190000')[0];
if (account) {
account.vpainfo.push(newdata);
}
console.log(original);

Add item into array instead of overriding the current value with the new value

I'm trying to figure out how to add new items into array instead of overriding the current value with the new value. I'm using .push() which should add the item every time it maps through the array. Any Ideas?
const searchChips = [
{value: "string"}, {value: "test"}
];
const query = {
bool: {
filter: []
}
};
const searchQuery = {
query_string: {
query: ""
}
};
searchChips.map(chip => {
console.log(chip);
const key = "query";
searchQuery.query_string[key] = chip.value;
query.bool.filter.push(searchQuery);
});
console.log(query);
You are inserting the same query since you are dealing with the same exact reference to the searchQuery. Instead of this try having it as a function which returns an object:
const searchChips = [{
value: "string"
}, {
value: "test"
}];
const query = {
bool: {
filter: []
}
};
let sq = (query) => ({
query_string: {query}
});
searchChips.map(chip => query.bool.filter.push(sq(chip.value)));
console.log(query);
This will return to you the 2 filters each with different values for query_string since now the function will return an entirely new object instead of you dealing with the same reference.
The problem seems to be that you are pushing into query.bool.filter outside the .map() function. Try this.
const searchChips = [{ value: "string" }, { value: "test" }];
const query = {
bool: {
filter: []
}
};
searchChips.forEach(chip => {
const key = "query";
const searchQuery = {
query_string: {
query: ""
}
};
searchQuery.query_string[key] = chip.value;
query.bool.filter.push(searchQuery);
});
console.log(JSON.stringify(query));

JS array key dynamic

I have an api data
current: {
errors: {},
items: {
Economy: {}
}
}
Object key "Economy" can be different, for instance "Advance"
and i call it like
let current = current.items.Economy
or let current = current.items.Advance
How can i call it dynamically?
P.S. My front don't know what key will be return
Use Object.entries to get every key and value pair in an object. If items only has one such key-value pair, then just select the first entry:
const obj = {
current: {
errors: {},
items: {
Foo: {data: 'foo data'}
}
}
};
const [key, val] = Object.entries(obj.current.items)[0];
console.log(key);
console.log(val);
If you don't actually care about the dynamic key name and just want the inner object, use Object.values instead.
You can use Object.keys(current.items).
let current = {
errors: {},
items: {
Economy: {age: 10}
}
}
let keys = Object.keys(current.items);
let currentVal = current.items[keys[0]];
console.log(currentVal);
You can also use for loop:
let current = {
errors: {},
items: {
Economy: {age: 10}
}
}
for(var key in current.items){
console.log(current.items[key]);
}

A more aesthetic way of copying an object literal?

This is client side. Webpack, Babel and Babel Imports.
My project has a folder called "models" which contains object literals as definitions of the expected JSON results from endpoints.
The objects only contain strings, ints, booleans and arrays/objects which contain those data types
eg:
{
name: "String"
age: 35,
active: true,
permissions: [
{ news: true }
]
}
When I want to use a model definition, in order to ensure I don't have issues with references, I must use:
let newObject1 = Object.assign({}, originalObj )
or
let newObject2 = JSON.parse( JSON.stringify( originalObj ))
I find this a bit ugly and it pollutes my code a bit.
I would love the ability to use the new keyword on object literals, but of course that's not a thing.
let clone = new targetObj
What's the most aesthetic way to handle the cloning of an object literal without creating a reference?
The JavaScript way of implementing such object "templates" are constructors:
function Original() {
this.name = "String";
this.age = 18;
this.active = true;
this.permissions = [
{ news: true }
];
}
var obj = new Original();
console.log(obj);
Or, in ES6 class syntax:
class Original {
constructor() {
this.name = "String";
this.age = 18;
this.active = true;
this.permissions = [
{ news: true }
];
}
}
let obj = new Original();
console.log(obj);
Be aware that Object.assign will only create a shallow copy, so it would not copy the permissions array, but provide a reference to the same array as in the original object.
const originalObject = {
name: "String",
age: 35,
active: true,
permissions: [
{ news: true }
]
};
let obj1 = Object.assign({}, originalObject);
let obj2 = Object.assign({}, originalObject);
// change a permission:
obj1.permissions[0].news = false;
// See what permissions are in obj2:
console.log(obj1.permissions);
const model = () => ({
name: 'string',
age: 20,
array: [ 1, 2, 3 ]
});
let newObject = model();
You won't have the pleasure of using new - see trincot's answer for that - but you don't have to worry about nested objects (assign) or feel gross (stringify + parse).

Remove value from object without mutation

What's a good and short way to remove a value from an object at a specific key without mutating the original object?
I'd like to do something like:
let o = {firstname: 'Jane', lastname: 'Doe'};
let o2 = doSomething(o, 'lastname');
console.log(o.lastname); // 'Doe'
console.log(o2.lastname); // undefined
I know there are a lot of immutability libraries for such tasks, but I'd like to get away without a library. But to do this, a requirement would be to have an easy and short way that can be used throughout the code, without abstracting the method away as a utility function.
E.g. for adding a value I do the following:
let o2 = {...o1, age: 31};
This is quite short, easy to remember and doesn't need a utility function.
Is there something like this for removing a value? ES6 is very welcome.
Thank you very much!
Update:
You could remove a property from an object with a tricky Destructuring assignment:
const doSomething = (obj, prop) => {
let {[prop]: omit, ...res} = obj
return res
}
Though, if property name you want to remove is static, then you could remove it with a simple one-liner:
let {lastname, ...o2} = o
The easiest way is simply to Or you could clone your object before mutating it:
const doSomething = (obj, prop) => {
let res = Object.assign({}, obj)
delete res[prop]
return res
}
Alternatively you could use omit function from lodash utility library:
let o2 = _.omit(o, 'lastname')
It's available as a part of lodash package, or as a standalone lodash.omit package.
With ES7 object destructuring:
const myObject = {
a: 1,
b: 2,
c: 3
};
const { a, ...noA } = myObject;
console.log(noA); // => { b: 2, c: 3 }
one line solution
const removeKey = (key, {[key]: _, ...rest}) => rest;
Explanations:
This is a generic arrow function to remove a specific key. The first argument is the name of the key to remove, the second is the object from where you want to remove the key. Note that by restructuring it, we generate the curated result, then return it.
Example:
let example = {
first:"frefrze",
second:"gergerge",
third: "gfgfg"
}
console.log(removeKey('third', example))
/*
Object {
first: "frefrze",
second: "gergerge"
}
*/
To add some spice bringing in Performance. Check this thread bellow
https://github.com/googleapis/google-api-nodejs-client/issues/375
The use of the delete operator has performance negative effects for
the V8 hidden classes pattern. In general it's recommended do not use
it.
Alternatively, to remove object own enumerable properties, we could
create a new object copy without those properties (example using
lodash):
_.omit(o, 'prop', 'prop2')
Or even define the property value to null or undefined (which is
implicitly ignored when serializing to JSON):
o.prop = undefined
You can use too the destructing way
const {remov1, remov2, ...new} = old;
old = new;
And a more practical exmple:
this._volumes[this._minCandle] = undefined;
{
const {[this._minCandle]: remove, ...rest} = this._volumes;
this._volumes = rest;
}
As you can see you can use [somePropsVarForDynamicName]: scopeVarName syntax for dynamic names. And you can put all in brackets (new block) so the rest will be garbage collected after it.
Here a test:
exec:
Or we can go with some function like
function deleteProps(obj, props) {
if (!Array.isArray(props)) props = [props];
return Object.keys(obj).reduce((newObj, prop) => {
if (!props.includes(prop)) {
newObj[prop] = obj[prop];
}
return newObj;
}, {});
}
for typescript
function deleteProps(obj: Object, props: string[]) {
if (!Array.isArray(props)) props = [props];
return Object.keys(obj).reduce((newObj, prop) => {
if (!props.includes(prop)) {
newObj[prop] = obj[prop];
}
return newObj;
}, {});
}
Usage:
let a = {propH: 'hi', propB: 'bye', propO: 'ok'};
a = deleteProps(a, 'propB');
// or
a = deleteProps(a, ['propB', 'propO']);
This way a new object is created. And the fast property of the object is kept. Which can be important or matter. If the mapping and the object will be accessed many many times.
Also associating undefined can be a good way to go with. When you can afford it. And for the keys you can too check the value. For instance to get all the active keys you do something like:
const allActiveKeys = Object.keys(myObj).filter(k => myObj[k] !== undefined);
//or
const allActiveKeys = Object.keys(myObj).filter(k => myObj[k]); // if any false evaluated value is to be stripped.
Undefined is not suited though for big list. Or development over time with many props to come in. As the memory usage will keep growing and will never get cleaned. So it depend on the usage. And just creating a new object seem to be the good way.
Then the Premature optimization is the root of all evil will kick in. So you need to be aware of the trade off. And what is needed and what's not.
Note about _.omit() from lodash
It's removed from version 5. You can't find it in the repo. And here an issue that talk about it.
https://github.com/lodash/lodash/issues/2930
v8
You can check this which is a good reading https://v8.dev/blog/fast-properties
As suggested in the comments above if you want to extend this to remove more than one item from your object I like to use filter. and reduce
eg
const o = {
"firstname": "Jane",
"lastname": "Doe",
"middlename": "Kate",
"age": 23,
"_id": "599ad9f8ebe5183011f70835",
"index": 0,
"guid": "1dbb6a4e-f82d-4e32-bb4c-15ed783c70ca",
"isActive": true,
"balance": "$1,510.89",
"picture": "http://placehold.it/32x32",
"eyeColor": "green",
"registered": "2014-08-17T09:21:18 -10:00",
"tags": [
"consequat",
"ut",
"qui",
"nulla",
"do",
"sunt",
"anim"
]
};
const removeItems = ['balance', 'picture', 'tags']
console.log(formatObj(o, removeItems))
function formatObj(obj, removeItems) {
return {
...Object.keys(obj)
.filter(item => !isInArray(item, removeItems))
.reduce((newObj, item) => {
return {
...newObj, [item]: obj[item]
}
}, {})
}
}
function isInArray(value, array) {
return array.indexOf(value) > -1;
}
My issue with the accepted answer, from an ESLint rule standard, if you try to destructure:
const { notNeeded, alsoNotNeeded, ...rest } = { ...ogObject };
the 2 new variables, notNeeded and alsoNotNeeded may throw a warning or error depending on your setup since they are now unused. So why create new vars if unused?
I think you need to use the delete function truly.
export function deleteKeyFromObject(obj, key) {
return Object.fromEntries(Object.entries(obj).filter(el => el[0] !== key))
}
with lodash cloneDeep and delete
(note: lodash clone can be used instead for shallow objects)
const obj = {a: 1, b: 2, c: 3}
const unwantedKey = 'a'
const _ = require('lodash')
const objCopy = _.cloneDeep(obj)
delete objCopy[unwantedKey]
// objCopy = {b: 2, c: 3}
For my code I wanted a short version for the return value of map() but the multiline/mutli operations solutions were "ugly". The key feature is the old void(0) which resolve to undefined.
let o2 = {...o, age: 31, lastname: void(0)};
The property stays in the object:
console.log(o2) // {firstname: "Jane", lastname: undefined, age: 31}
but the transmit framework kills it for me (b.c. stringify):
console.log(JSON.stringify(o2)) // {"firstname":"Jane","age":31}
I wrote big function about issue for me. The function clear all values of props (not itself, only value), arrays etc. as multidimensional.
NOTE: The function clear elements in arrays and arrays become an empty array. Maybe this case can be added to function as optional.
https://gist.github.com/semihkeskindev/d979b169e4ee157503a76b06573ae868
function clearAllValues(data, byTypeOf = false) {
let clearValuesTypeOf = {
boolean: false,
number: 0,
string: '',
}
// clears array if data is array
if (Array.isArray(data)) {
data = [];
} else if (typeof data === 'object' && data !== null) {
// loops object if data is object
Object.keys(data).forEach((key, index) => {
// clears array if property value is array
if (Array.isArray(data[key])) {
data[key] = [];
} else if (typeof data[key] === 'object' && data !== null) {
data[key] = this.clearAllValues(data[key], byTypeOf);
} else {
// clears value by typeof value if second parameter is true
if (byTypeOf) {
data[key] = clearValuesTypeOf[typeof data[key]];
} else {
// value changes as null if second parameter is false
data[key] = null;
}
}
});
} else {
if (byTypeOf) {
data = clearValuesTypeOf[typeof data];
} else {
data = null;
}
}
return data;
}
Here is an example that clear all values without delete props
let object = {
name: 'Semih',
lastname: 'Keskin',
brothers: [
{
name: 'Melih Kayra',
age: 9,
}
],
sisters: [],
hobbies: {
cycling: true,
listeningMusic: true,
running: false,
}
}
console.log(object);
// output before changed: {"name":"Semih","lastname":"Keskin","brothers":[{"name":"Melih Kayra","age":9}],"sisters":[],"hobbies":{"cycling":true,"listeningMusic":true,"running":false}}
let clearObject = clearAllValues(object);
console.log(clearObject);
// output after changed: {"name":null,"lastname":null,"brothers":[],"sisters":[],"hobbies":{"cycling":null,"listeningMusic":null,"running":null}}
let clearObject2 = clearAllValues(object);
console.log(clearObject2);
// output after changed by typeof: {"name":"","lastname":"","brothers":[],"sisters":[],"hobbies":{"cycling":false,"listeningMusic":false,"running":false}}

Categories