A more succinct way to transform JS object? - javascript

Please see code below. Is there a more succinct way to transform the originalObject into myObject?
The key is that I'm trying to set myObject's property names based on the id within each property in originalObject.
myObject = Object
.values( originalObject )
.reduce( (acc, x) => {
let propHolder = {}
propHolder[x.id] = false
return Object.assign( acc, propHolder )
}, {} )

Since you have let, you have computed properties:
myObject = Object.values(originalObject).reduce((acc, x)=>{
return Object.assign(acc, {[x.id]: false})
},{})
Now since the arrow function is just a return:
myObject =
Object.values(originalObject)
.reduce((acc, x) => Object.assign(acc, {[x.id]: false}), {})
and if you don’t mind making an array:
myObject =
Object.values(originalObject)
.map(x => ({[x.id]: false}))
.reduce(Object.assign, {});
I would prefer to have a fromPairs function on hand, though.
myObject = fromPairs(
Object.values(originalObject)
.map(x => [x.id, false])
);
If you use Lodash’s fromPairs, you can also use its map.
myObject = _.fromPairs(
_.map(originalObject, x => [x.id, false])
);

See Ryan’s answer for elegant but long one-liners. But since you were asking for a succinct way, here's the simple solution:
myObject = {};
for (const k in originalObject)
myObject[originalObject[k].id] = false;
Or possibly more interesting:
myObject = {};
for (const {id} of Object.values(originalObject))
myObject[id] = false;

You can make use of Javascript's spread syntax:
const myObject = Object
.values(originalObject)
.reduce((acc, x) => ({ ...acc, [x.id]: false }), {})
More reading: Spread syntax

Related

Modify object value in array inside map

Trying to modify object value in array on condition on some arrays the override does not work, here is my snippet
const removeAction = (target, array, name) => {
let mutation = JSON.parse(JSON.stringify(array));
mutation.map( obj => {
if(obj.value === target.value) {
console.log(obj)
obj.checked = false
}
return obj
})
console.log(mutation)
removeCallback(mutation, name)
}
and I get back the original object in array. How do I debug this issue?
const modifiedArray = JSON.parse(JSON.stringify(array)).map(...
Map always returns new instance of array. So either assign that mutated map to some variable or loop over and mutate the original array.
You could take the mapped objects with destructuring the object and an updated property.
const
removeAction = (target, array, name) => {
removeCallback(array.map(obj => ({
...obj,
checked: obj.value === target.value ? false : obj.checked
})), name)
};

Assign object values conditionally JavaScript

I am receiving a data object from an api where some of its properties may have a value of null. I want to create a function that returns the object but for the properties that have a null value there is a "-" instead. I have tried:
const hyphenOrContent = movie => {
return Object.values(movie).map(val => {
return val === null ? "-" : val
});
}
but this only returns an array of the values. I've read that it's better not to use a for in loop so I would like to avoid using one if possible. Any help appreciated!
Map the object's entries, turning nulls into -s, then use Object.fromEntries to turn the array of entries into an object:
const hyphenOrContent = movie => Object.fromEntries(
Object.entries(movie).map(
([key, val]) => ([key, val === null ? "-" : val])
)
);
Just loop over the Object and alter the properties.
const hyphenOrContent = movie => {
Object.entries(movie).forEach(([key, val]) => {
if (val === null) movie[key] = '-';
});
};
var a = {
foo: null,
bar: '123',
baz: null
}
hyphenOrContent(a);
console.log(a);
If you do not want to alter the original, you can clone it.

Optimization of double for with .map / .filter / .reduce or something

Is there a way to optimize the following with ES5 / ES6?
var obj = [];
var set;
for (var key_s in data1) {
set = false;
for (var key_s2 in data2) {
if (data1[key_s].id === data2[key_s2].id) {
obj.push({'id': data1[key_s].id,
'key': data1[key_s].key,
'value': data2[key_s2].value})
set = true;
break;
}
}
if (!set)
obj.push({'id': data1[key_s].id,
'key': data1[key_s].key,
'value': "EMPTY"})
}
I want to have an object which contains all keys of data1 and if a pair got the same id like the one in data2, it should take its value, otherwise it should become 'EMPTY'. Or maybe is there a way to use Object.assign with some special parameters or something?
You could use Map for data2 and use Array#map with data1 and the possible value of data2.
var map = new Map(data2.map(o => ([o.id, o]))),
obj = data1.map(({ id, key }) => ({ id, key, value: map.has(id)
? map.get(id).value
: 'EMPTY'
}));
This is pretty es6'ed. map over data1, the .find will return the matching object if one exists or else return undefined. set key_s.value to either the key_s2 value or "EMPTY" and then return the key_s object. You will end up with an array of objects. This assumes that data1 and data2 are arrays of objects, that is how your example was written so I figured it was the case.
let obj = data1.map(key_s => {
let newValue = data2.find(key_s2 => key_s.id === key_s2.id)
key_s.value = newValue ? newValue.value : "EMPTY"
return key_s
})
})
You could do it with Array#map, Array#find and Object.assign. By specifying the value in the first parameter of Object.assign, you can set a default to be overridden by the object from data2.
var obj = data1.map(obj1 => {
const obj2 = data2.find(obj2 => obj1.id === obj2.id);
return Object.assign({ value: 'EMPTY' }, obj1, obj2);
});
Using spread properties, that Object.assign could be changed to:
{ value: 'EMPTY', ...obj1, ...obj2 }

ES6 copy properties to target only if they already exist

Essentially what I want
let schema = {
name: null,
lastname: null
}
let values = {
name: "John",
unwanted: "haxor"
}
to end up in:
console.log(sanitized); // {name: "John", lastname: null}
--
Using Object.assign(schema, values) ends up with the unwanted value.
Is there a simple way?
Edit: I should add, this is to avoid using a library like lodash or underscore, so if the solution is overly complex, they would be preferable solutions.
There is no builtin method which achieves that. However, there is a simple (almost trivial) way to do it:
const sanitized = {};
for (const p in schema)
sanitized[p] = (p in object ? object : schema)[p];
Just retrieve the same key from the other object:
Object.keys(schema).forEach(key => schema[key] = (key in values ? values : schema)[key]);
If you want to create a new object:
var newObj = {};
Object.keys(schema).forEach(key => newObj[key] = (key in values ? values : schema)[key]);
Also, this is compatible with previous version of ES as well (if you do not use arrow function, of course).
The (key in values ? values : schema)[key]) part assures properties that are only in first schema aren't set to undefined
Edited to OP's slightly more complicated request
Just map the value's and schema's keys to individual objects, and spread them. Prioritize values by placing them after:
Object.assign(
schema,
...Object.keys(schema).map(
(key) => ({[key]: schema[key]})
),
...Object.keys(schema).map(
(key) => ({[key]: values[key]})
)
);
If you don't want to overwrite schema, specify a different target for Object.assign():
let sanitized = Object.assign(
{},
...Object.keys(schema).map(
(key) => ({[key]: schema[key]})
),
...Object.keys(schema).map(
(key) => ({[key]: values[key]})
)
);
Following a pattern closer to #Bergi's answer, you could do something less verbose like this with Object.assign():
let sanitized = Object.assign({}, ...Object.keys(schema).map(
(key) => ({[key]: (key in values ? values : schema)[key]})
)
);

How to replace/name keys in a Javascript key:value object?

How should I replace the key strings in a Javascript key:value hash map (as an object)?
This is what I have so far:
var hashmap = {"aaa":"foo", "bbb":"bar"};
console.log("before:");
console.log(hashmap);
Object.keys(hashmap).forEach(function(key){
key = key + "xxx";
console.log("changing:");
console.log(key);
});
console.log("after:");
console.log(hashmap);
See it running in this jsbin.
The "before" and "after" hashmaps are the same, so the forEach seems to be in a different scope. How can I fix it? Perhaps there are better ways of doing this?
It has nothing to do with scope. key is just a local variable, it's not an alias for the actual object key, so assigning it doesn't change the object.
Object.keys(hashmap).forEach(function(key) {
var newkey = key + "xxx";
hashmap[newkey] = hashmap[key];
delete hashmap[key];
});
if keys order is important you can use:
const clone = Object.fromEntries(
Object.entries(o).map(([o_key, o_val]) => {
if (o_key === key) return [newKey, o_val];
return [o_key, o_val];
})
);
this will create an object with the new key in the same place where the old one was.
You are just changing the copy of the object's keys, so the original object won't be changed. You can create an new object to hold the new keys, like this:
var hashmap = {"aaa":"foo", "bbb":"bar"};
console.log("before:");
console.log(hashmap);
var newHashmap = {};
Object.keys(hashmap).forEach(function(key){
var value = hashmap[key];
key = key + "xxx";
console.log("changing:");
console.log(key);
newHashmap[key] = value;
});
console.log("after:");
console.log(newHashmap);
You can use Array.prototype.reduce().
const hashmap = { aaa: 'foo', bbb: 'bar' };
const newHashmap = Object.entries(hashmap).reduce((acc, [key, value]) => ({
...acc,
[`${key}xxx`]: value,
}), {});
console.log(newHashmap);
// { aaaxxx: 'foo', bbbxxx: 'bar' }
The function takes as an argument the original map and returns a new one with the keys altered. The call of the mapT(m) just returns the transformed map with the new keys.
function mapT(map){
const x = new Map();
for (const [k, v] of map) {
x.set(k+ "xxx", v);
}
return x;
}
Simple call : var new_map = mapT(mapA);

Categories

Resources