Override all keys of an object but its id - javascript

I have a similar object as this one but it has quite more keys. I want to update all of its keys but id. I can do so manually. But I think it is not the best way.
const o = {
name: "unknow",
key: "key"
value: "value"
id ": 12
}
How can I update/override all keys of an object but id?
Update
The two object has the same keys. But their keys have different value. I need to update all keys of the first object excluding its id.

I suspect that you're looking for something like assignBut: it sets properties of ob on oa but the specified one:
const assignBut = (prop, oa, ob) => {
for (let key of Object.keys(ob))
// Check that I also verify that the property
// to set should be part of "oa" object. This
// prevents adding new properties: it just updates
// existing ones.
if (key !== prop && oa.hasOwnProperty(key))
oa[key] = ob[key]
}
const oa = {
name: "unknow",
key: "key",
value: "value",
id: 12
}
const ob = {
name: "xxx",
key: "yyy",
value: "zzz",
other: "aaa",
yetAnother: 289,
id: 15
}
assignBut('id', oa, ob)
console.log(oa)
Another approach to omit a given property
One may take advantage of destructuring and computed property names to omit the whole given property so the for..of just needs to check that each property from ob is present in oa to set it.
Also, one may save the check to verify that a property from ob exists in oa performing an intersection of oa and ob keys:
const oa = {
name: "unknow",
key: "key",
value: "value",
id: 12
}
const ob = {
name: "xxx",
key: "yyy",
value: "zzz",
other: "aaa",
yetAnother: 289,
id: 15
}
const intersect = (xs, ys) => xs.filter(x => ys.includes(x))
const assignBut = (prop, oa, {
[prop]: omitted,
...ob
}) => {
const sharedKeys = intersect(Object.keys(oa), Object.keys(ob))
for (let key of sharedKeys)
oa[key] = ob[key]
}
assignBut('id', oa, ob)
console.log(oa)

You can iterate through Object.keys like below -
const o = {
name: "unknow",
key: "key",
value: "value",
id : 12
};
Object.keys(o).forEach((key)=>{
if(key !=="id"){
console.log(o[key]) //value
}
}
);

Following approach is based on lodash. If you are not comfortable using a library, please ignore.
Benefit of omit is that you can pass an array of keys and ignore multiple keys.
There is also a function called pick where you can only pick certain properties you need.
_.omit
const o = { name: "unknow", key: "key", value: "value", id: 12 }
const props = { name: "foo", key: "key2", value: "bar", id: 15 };
const final = _.assign({}, o, _.omit(props, 'id'));
console.log(final)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
_.pick
const o = { name: "unknow", key: "key", value: "value", id: 12 }
const props = { name: "foo", key: "key2", value: "bar", id: 15, test: 'abc', hello: 'world' };
const final = _.assign({}, o, _.pick(props, ['name', 'key', 'value']));
console.log(final)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
pure js implementation
const o = { name: "unknow", key: "key", value: "value", id: 12 }
const propsOmit = { name: "foo", key: "key2", value: "bar", id: 15 };
const propsPick = { name: "foo", key: "key2", value: "bar", id: 15, test: 'abc', hello: 'world' };
const finalOmit = Object.assign({}, o, omit(propsOmit, 'id'));
const finalPick = Object.assign({}, o, omit(propsPick, ['id', 'test', 'hello']));
console.log(finalOmit)
console.log(finalPick)
function omit(obj, ignoreKeys) {
if (!Array.isArray(ignoreKeys)) {
ignoreKeys = [ ignoreKeys ];
}
const copy = Object.assign({}, obj);
ignoreKeys.forEach((k) => delete copy[k]);
return copy;
}
function pick(obj, selectKeys) {
if (!Array.isArray(selectKeys)) {
selectKeys = [ selectKeys ];
}
const copy = {};
ignoreKeys.forEach((k) => copy[k] = obj[k]);
return copy;
}
References:
_.assign
_.omit
_.pick

Related

JavaScript: How to merge two object but keep only the property of the second object if it exists?

This code merges two objects. How to amend it so that a property is discarded of the first object if it exists in the second object?
E.g. object 1 has this property:
surname: {
test: '123',
other: '124'
},
and object 2:
surname: {
test: '124'
},
The merge would use the property of object 2 (other: '124' would not exist in the merged result)
function merge(...objects) {
function m(t, s) {
Object.entries(s).forEach(([k, v]) => {
t[k] = v && typeof v === 'object' ? m(t[k] || {}, v) : v;
});
return t;
}
return objects.reduce(m, {});
}
var obj1 = {
name: '112',
surname: {
test: '123',
other: '124'
},
age: 151,
height: '183',
weight: 80
},
obj2 = {
name: '114',
surname: {
test: '124'
},
age: 151,
height: 184,
weight: 81
},
result = merge(obj1, obj2);
console.log(result);
const a = { x:1, y:2}
const b = { x:5 }
const c = { ...a, ...b }
console.log(c)
// Answer will be {x: 5, y: 2}
You seem to want to do a shallow merge but with recreated objects to avoid references to the original ones.
A quick way for this is to use Object.assign for the merge, and then do a json stringify/parse (with the limitations of what JSON supports) to clone the result and lose the references (you could alternatively use structuredCloned if your environment/browser supports it).
So something like
function merge(...objects) {
const shallowClone = Object.assign({}, ...objects);
const asString = JSON.stringify(shallowClone);
const asObject = JSON.parse(asString);
return asObject;
}

Can we assign an object key to another variable like this? [duplicate]

Given a JavaScript object, how can I convert it into an array of objects (each with key, value)?
Example:
var data = { firstName: 'John', lastName: 'Doe', email: 'john.doe#gmail.com' }
resulting like:
[
{ key: 'firstName', value: 'John' },
{ key: 'lastName', value: 'Doe' },
{ key: 'email', value: 'john.doe#gmail.com' }
]
var data = { firstName: 'John', lastName: 'Doe', email: 'john.doe#gmail.com' }
var output = Object.entries(data).map(([key, value]) => ({key,value}));
console.log(output);
Inspired By this post
Using map function
var data = { firstName: 'John', lastName: 'Doe', email: 'john.doe#gmail.com' };
var result = Object.keys(data).map(key => ({ key, value: data[key] }));
console.log(result);
You can just iterate over the object's properties and create a new object for each of them.
var data = { firstName: 'John', lastName: 'Doe', email: 'john.doe#gmail.com' };
var result = [];
for(var key in data)
{
if(data.hasOwnProperty(key))
{
result.push({
key: key,
value: data[key]
});
}
}
The previous answer lead me to think there is a better way...
Object.keys(data).map(function(key) {
return { key, value: data[key] };
});
or in ES6 using arrow functions:
Object.keys(data).map((key) => ({ key, value: data[key] }));
Just make your life easier and use es6 syntax with a map
var output = Object.keys(data).map(key => {
return {
key: key,
value: data[key]
};
})
var result = [];
for(var k in data) result.push({key:k,value:data[k]});
Or go wild and make the key and value keys customizable:
module.exports = function objectToKeyValueArray(obj, keyName = 'key', valueName = 'value') {
return Object
.keys(obj)
.filter(key => Object.hasOwnProperty.call(obj, key))
.map(key => {
const keyValue = {};
keyValue[keyName] = key;
keyValue[valueName] = obj[key];
return keyValue;
});
};
An alternative method for doing this that works on multi level objects and does not use recursion.
var output = []
var o = {
x:0,
y:1,
z:{
x0:{
x1:4,
y1:5,
z1:6
},
y0:2,
z0:[0,1,2],
}
}
var defer = [ [ o ,[ '_root_' ] ] ]
var _defer = []
while(defer.length){
var current = defer.pop()
var root = current[1]
current = current[0]
for(var key in current ){
var path = root.slice()
path.push(key)
switch( current[key].toString() ){
case '[object Object]':
_defer.push( [ current[key] , path ] )
break;;
default:
output.push({
path : path ,
value : current[key]
})
break;;
}
}
if(!defer.length)
defer = _defer.splice(0,_defer.length)
}
[
{ path: [ '_root_', 'x' ], value: 0 },
{ path: [ '_root_', 'y' ], value: 1 },
{ path: [ '_root_', 'z', 'y0' ], value: 2 },
{ path: [ '_root_', 'z', 'z0' ], value: [ 0, 1, 2 ] },
{ path: [ '_root_', 'z', 'x0', 'x1' ], value: 4 },
{ path: [ '_root_', 'z', 'x0', 'y1' ], value: 5 },
{ path: [ '_root_', 'z', 'x0', 'z1' ], value: 6 }
]
const array = [
{ key: "key1", value: "value1" },
{ key: "key2", value: "value2" },
];
const obj = Object.fromEntries(array.map(item => [item.key, item.value]));
console.log(obj);
I would say to use npm package flat.
works amazing for nested objects and arrays.
var flatten = require('flat')
flatten({
key1: {
keyA: 'valueI'
},
key2: {
keyB: 'valueII'
},
key3: { a: { b: { c: 2 } } }
})
// {
// 'key1.keyA': 'valueI',
// 'key2.keyB': 'valueII',
// 'key3.a.b.c': 2
// }
const array = [
{ key: "key1", value: "value1" },
{ key: "key2", value: "value2" },
];
const obj = Object.fromEntries(array.map(item => [item.key, item.value]));
console.log(obj);

How to convert array to object by key?

Suppose I got this array:
const users =[
{
id:1,
name:'bob',
},
{
id:2,
name:'sally',
},
{
id:3,
name:'bob',
age:30,
}
];
And I want to use any key(in this case 'name' ) to return an object :
{
bob:[
{
id:1,
name:'bob',
},
{
id:3,
name:'bob',
age:30,
}
],
sally:[
{
id:2,
name:'sally',
}
],
}
I tried this:
const go = (A,key) =>{
return A.reduce((o, key) => ({ ...o, [key]:o }), {})
}
export default go;
But this returns:
{ '[object Object]': { '[object Object]': { '[object Object]': {} } } }
If the key is not present omit from the result. It should not mutate the original array though. How can I perform this kind of conversion?
With the approach you have, a new array is not instantiated in case the key is not yet present in the object.
This will work:
const result = users.reduce((a, v) => {
a[v.name] = a[v.name] || [];
a[v.name].push(v);
return a;
}, {});
Complete snippet wrapping this logic in a function:
const users = [{
id: 1,
name: 'bob',
}, {
id: 2,
name: 'sally',
}, {
id: 3,
name: 'bob',
age: 30,
}];
const go = (input, key) => input.reduce((a, v) => {
a[v[key]] = a[v[key]] || [];
a[v[key]].push(v);
return a;
}, {});
console.log(go(users, 'name'));
If you really want to cram it into a one-liner, this will also work, by either spreading the already existing array, or an empty one:
const result = users.reduce((a, v) => ({...a, [v.name]: [...a[v.name] || [], v]}), {});
Complete snippet wrapping this logic in a function:
const users = [{
id: 1,
name: 'bob',
}, {
id: 2,
name: 'sally',
}, {
id: 3,
name: 'bob',
age: 30,
}];
const go = (input, key) => input.reduce((a, v) => ({...a, [v[key]]: [...a[v[key]] || [], v]}), {});
console.log(go(users, 'name'));
You were close but the key attribute in this case was each value (eg: { id: 1, name: 'bob' }) so the string representation is [object Object] which is why all the keys are that. Based off what you said, you want to use key.name as the property and set it's value as [key]. (I renamed key to arr in my example since it's the array value).
So this would be something like { ...o, [arr.name]: [arr] }
Because there can be an existing value, it adds a bit of complexity which is what [...(obj[arr.name] || []), arr] is doing. It's looking up the existing value (or defaulting to an empty array) and spreading those values and adding the new value.
const users = [{
id: 1,
name: 'bob',
},
{
id: 2,
name: 'sally',
},
{
id: 3,
name: 'bob',
age: 30,
}
];
const transform = (input, keyName) => {
return input.reduce((obj, arr) => ({ ...obj,
[arr[keyName]]: [...(obj[arr[keyName]] || []), arr]
}), {})
}
console.log(transform(users, 'name'))
console.log(transform(users, 'id'))

Convert a key-value array with duplicate keys into array of object with unique key, and value array property

I have an array of key/value pairs. The keys are sometimes duplicated, and the values are always unique per key.
I want to condense each unique key to an object, so that I have a key and an array of the associated values as a property.
Are there any handy javascript functions to do this?
This
pairArray = [
{ key: "a", value: "1" },
{ key: "a", value: "2" },
{ key: "b", value: "1" },
{ key: "b", value: "2" },
];
Becomes
objectArray = [
{ key: "a", values: ["1", "2"] },
{ key: "(", values: ["1", "2"] }
];
You can simply create a map using Array.reduce() with your key property of your object as key of your map, Object.values() on that map will give you the desired result :
Assuming you have a typo in your expected output. You can try the following :
const pairArray = [ { key: "a", value: "1" }, { key: "a", value: "2" }, { key: "b", value: "1" }, { key: "b", value: "2" }, ];
const result = Object.values(pairArray.reduce((acc, {key, value})=>{
acc[key] = acc[key] || {key, values : []};
acc[key].values.push(value);
return acc;
},{}));
console.log(result);
You can use Map() to get the desired output:
let data = [
{ key: "a", value: "1" },
{ key: "a", value: "2" },
{ key: "b", value: "1" },
{ key: "b", value: "2" },
];
let reducer = arr => {
let map = new Map();
arr.forEach(({key:k, value:v}) => {
let values = map.get(k) || [];
values.push(v);
map.set(k, values);
});
return [...map.entries()].map(([k, v]) => ({key: k, values: v}));
};
console.log(reducer(data));

How to get a single array with no-repeated items, with the sum of their value(s). ordered desc

If I have the following Array:
var myArray = [
{sa67g:{id:'sa67g', name: 'Leo', value: 50}},
{sa86w:{id:'sa86w', name: 'Amy', value: 40}},
{sa33p:{id:'sa33p', name: 'Alex', value: 30}},
{sa74x:{id:'sa74x', name: 'John', value: 20}},
{sa67g:{id:'sa67g', name: 'Leo', value: 10}},
{sa33p:{id:'sa33p', name: 'Alex', value: 15}},
]
What is the best one to get a single array with no-repeated items, with the sum of their value(s) and ordered by descending value for all the repeated items from another array using lodash?
Te expected result should be something like this:
result = [{sa67g:{id:'sa67g', name: 'Leo', value: 60}},
{sa33p:{id:'sa33p', name: 'Alex', value: 45}},
{sa86w:{id:'sa86w', name: 'Amy', value: 40}},
{sa74x:{id:'sa74x', name: 'John', value: 20}}
]
I'm sure there are a bunch of ways to do it. Off the top of my head, I would use reduce to convert your array of objects into one object with summed values. Using chain to combine with some other lodash methods for ordering and transforming, it would look like this:
const result = _.chain(myArray)
.map(person => _.values(person))
.flatten()
.reduce((summed, person) => {
if (!summed[person.id]) {
summed[person.id] = person
} else {
summed[person.id].value += person.value
}
return summed
}, {})
.values()
.orderBy(['value'], ['desc'])
.map(person => ({ [person.id]: person }))
.value()
You can use lodash's _.mergeWith() to combine all objects into a single object. Since _.mergeWith is recursive, the inner properties will be merged as well, and we can use this to sum the value property. Afterwards, we convert the object back to an array using _.map():
const myArray = [{"sa67g":{"id":"sa67g","name":"Leo","value":50}},{"sa86w":{"id":"sa86w","name":"Amy","value":40}},{"sa33p":{"id":"sa33p","name":"Alex","value":30}},{"sa74x":{"id":"sa74x","name":"John","value":20}},{"sa67g":{"id":"sa67g","name":"Leo","value":10}},{"sa33p":{"id":"sa33p","name":"Alex","value":15}}];
const result = _.map(
// merge all objects into a single object, and sum the value property
_.mergeWith({}, ...myArray, (objValue = 0, srcValue = 0, key) => key === 'value' ? objValue + srcValue : undefined),
// split back into an array of objects
(v, k) => ({ [k]: v })
)
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
Easiest way to filter out single values would be to use a Set. Use it to get a set of unique keys.
Create the desired object by iterating the keys and calculating the sums.
Using native methods and assuming es8 support:
var myArray = [
{sa67g:{id:'sa67g', name: 'Leo', value: 50}},
{sa86w:{id:'sa86w', name: 'Amy', value: 40}},
{sa33p:{id:'sa33p', name: 'Alex', value: 30}},
{sa74x:{id:'sa74x', name: 'John', value: 20}},
{sa67g:{id:'sa67g', name: 'Leo', value: 10}},
{sa33p:{id:'sa33p', name: 'Alex', value: 15}},
]
var tmp = myArray.map(o => Object.values(o)[0])
.reduce((a, c) => {
const obj = a[c.name]
if (obj) {
obj.id = c.id > obj.id ? c.id : obj.id;// set highest id
obj.value += c.value; //increment vlues
} else {
a[c.name] = Object.assign({},c);
}
return a;
}, {});
var res = Object.values(tmp)
.sort((a, b) => b.value - a.value)
.map(o => ({[o.id]:o }))
console.log(res)
.as-console-wrapper { max-height: 100%!important;}
You could take a step by step approach and take the resutl for the next function with a pipe.
var array = [{ sa67g: { id: 'sa67g', name: 'Leo', value: 50 } }, { sa86w: { id: 'sa86w', name: 'Amy', value: 40 } }, { sa33p: { id: 'sa33p', name: 'Alex', value: 30 } }, { sa74x: { id: 'sa74x', name: 'John', value: 20 } }, { sa67g: { id: 'sa67g', name: 'Leo', value: 10 } }, { sa33p: { id: 'sa33p', name: 'Alex', value: 15 } }],
pipe = (...fn) => arg => fn.reduce((x, f) => f(x), arg),
objects = array => array.map(o => Object.assign({}, Object.values(o)[0])),
sum = array => array.reduce((m, o) => {
if (m.has(o.id)) {
m.get(o.id).value += o.value;
} else {
m.set(o.id, o);
}
return m;
}, new Map),
values = map => Array.from(map.values()),
sort = array => array.sort((a, b) => b.value - a.value),
map = array => array.map(o => ({ [o.id]: o })),
path = pipe(objects, sum, values, sort, map),
result = path(array);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Object properties should never be a value (ie, a key or id) – to create a key-value association, use a Map instead.
const concat = ({ id: id1, name: name1, value: v1 }, { id: id2, name: name2, value: v2 }) =>
({ id: id1, name: name1, value: v1 + v2 })
const main = xs =>
xs.map (x => Object.entries (x) [ 0 ])
.reduce ((acc, [ key, value ]) =>
acc.has (key)
? acc.set (key, concat (value, acc.get (key)))
: acc.set (key, value), new Map)
const myArray =
[ {sa67g:{id:'sa67g', name: 'Leo', value: 50}}
, {sa86w:{id:'sa86w', name: 'Amy', value: 40}}
, {sa33p:{id:'sa33p', name: 'Alex', value: 30}}
, {sa74x:{id:'sa74x', name: 'John', value: 20}}
, {sa67g:{id:'sa67g', name: 'Leo', value: 10}}
, {sa33p:{id:'sa33p', name: 'Alex', value: 15}}
]
console.log (main (myArray))
// => Map {
// 'sa67g' => { id: 'sa67g', name: 'Leo', value: 60 },
// 'sa86w' => { id: 'sa86w', name: 'Amy', value: 40 },
// 'sa33p' => { id: 'sa33p', name: 'Alex', value: 45 },
// 'sa74x' => { id: 'sa74x', name: 'John', value: 20 } }
If you want to convert the Map back to an object, you can use this
const mapToObject = m =>
Array.from(m.entries ()).reduce ((acc, [ k, v ]) =>
Object.assign (acc, { [ k ]: v }), {})

Categories