Object rest/spread only excising properties - javascript

I was wondering if it with rest/spread is possible to only override the existing properties on an object:
let xy = {
x: 1,
y: 2,
}
let xyz = {
x: 41,
y: 23,
z: 1
}
Now i have two objects and i wish to override the existing properties on xy without getting the z property from xyz as well, so my output is the following:
xy = {
x: 41,
y: 23,
}
Is this possible?
Thanks in advance!

Rest/spread will always stuff into a destination what it finds in a source object, if you don't want to use a loop / want a functional approach, go for reduce, e.g.
const xyNew = Object.keys(xyz).reduce((res, key) =>
// if the key is contained in the accumulator, rewrite it with xyz value, else just return the accumulator (res ~ "result")
res[key] ? { ...res, [key]: xyz[key] } : res
, xy);

Here is a simple implementation :-)
let xy = {
x: 1,
y: 2,
}
let xyz = {
x: 41,
y: 23,
z: 1
}
function compute(){
let key = Object.keys(xy);
for(let i=0;i<key.length;i++){
if(key[i] in xyz) xy[key[i]] = xyz[key[i]];
}
}
compute();
console.log(xy);

Related

Targeting two instances of key-value pair in specific indices of nested array

Say I have a nested array something like
var allPairs = [
[
{ x: 1, y: 0 },
{ x: 1, y: 1 },
],
[
{ x: 1, y: 1 },
{ x: 1, y: 2 },
],
[
{ x: 1, y: 2 },
{ x: 2, y: 2 },
],
]
and I have an object
var currentPair = {x: 1, y: 1};
I've been struggling with trying to find a way to check if, in allPairs, currentPair equals the second index [1] of one nested array, while also equaling the first index [0] of another nested array.
I've been playing around with some:
if (arrOfPointsUsed.some(e => e[1] === firstPoint)) {
pointsFound++;
}
if (arrOfPointsUsed.some(e => e[0] === firstPoint)) {
pointsFound++;
}
if (pointsFound > 1) {
return false;
}
I've tried nested for loops also.
What is the best approach?
I think you're on the right track with some, but I think you'll need two calls to it:
const foundBoth =
// Found 0?
allPairs.some(([{x, y}]) => x === currentPair.x && y == currentPair.y)
&&
// Found 1?
allPairs.some(([, {x, y}]) => x === currentPair.x && y == currentPair.y)
;
Live Example:
var allPairs = [ [{x: 1, y: 0}, {x: 1, y: 1}], [{x: 1, y: 1}, {x: 1, y: 2}], [{x: 1, y: 2}, {x: 2, y: 2} ] ];
var currentPair = {x: 1, y: 1};
const foundBoth =
// Found 0?
allPairs.some(([{x, y}]) => x === currentPair.x && y == currentPair.y)
&&
// Found 1?
allPairs.some(([, {x, y}]) => x === currentPair.x && y == currentPair.y)
;
console.log(foundBoth);
Or to do it in one pass, you could use a loop:
let foundBoth = false;
let found0 = false;
let found1 = false;
for (const [e0, e1] of allPairs) {
found0 = found0 || e0.x === currentPair.x && e0.y === currentPair.y;
found1 = found1 || e1.x === currentPair.x && e1.y === currentPair.y;
foundBoth = found0 && found1;
if (foundBoth) {
break;
}
}
Live Example:
var allPairs = [ [{x: 1, y: 0}, {x: 1, y: 1}], [{x: 1, y: 1}, {x: 1, y: 2}], [{x: 1, y: 2}, {x: 2, y: 2} ] ];
var currentPair = {x: 1, y: 1};
let foundBoth = false;
let found0 = false;
let found1 = false;
for (const [e0, e1] of allPairs) {
found0 = found0 || e0.x === currentPair.x && e0.y === currentPair.y;
found1 = found1 || e1.x === currentPair.x && e1.y === currentPair.y;
foundBoth = found0 && found1;
if (foundBoth) {
break;
}
}
console.log(foundBoth);
...but allPairs would have to be massive for making one pass vs. two to make a difference.
Some notes on the above:
I've assumed that the currentPair object and the matching objects in allPairs are merely equivalent, not the same object. That's why I'm checking x and y, not === on the object themselves. (Because two separate objects are never === one another.)
I'm using destructuring in various places, both iterable destructuring to pick out the 0th and 1st entries from each of the subarrays and object destructuring to pick out the x and y properties of those objects. In the first example (using some), [{x, y}] in the first some call's callback function's parameter list picks the x and y properties out of the first entry in the subarray, and [,{x, y}] does the same for the second (skipping the first entry with the , at the beginning).
If it were even one tiny bit more complex to compare the objects than just the x and y checks above, I'd factor that comparison operation out into its own function and reuse it.
In a comment you've asked:
Thanks for your thorough answer. Just trying to understand your syntax in the first example: how would I, for instance, find if the first index is equivalent to currentPair in more than one array within allPairs
For that I'd probably use find or findIndex twice: The first time to find the first index, the second time to find another one that isn't the first one. For instance:
const firstIndex = allpairs.findIndex(([{x, y}]) => x === currentPair.x && y == currentPair.y);
const secondIndex = firstIndex === -1 ? -1 : allpairs.findIndex(([{x, y}], index) => index > firstIndex && x === currentPair.x && y == currentPair.y);
if (secondIndex !== -1) {
// Found in the first entry of two different subarrays
}
You need to check the values instead of the object reference.
For the check, you need the entries of the target object and iterate this as well.
const
allPairs = [[{ x: 1, y: 0 }, { x: 1, y: 1 }], [{ x: 1, y: 1 }, { x: 1, y: 2 }], [{ x: 1, y: 2 }, { x: 2, y: 2 }]],
currentPair = { x: 1, y: 1 },
entries = Object.entries(currentPair),
result = allPairs.some(array =>
array.some(object =>
entries.every(([key, value]) => object[key] === value)
)
);
console.log(result);

Return a random Object containing a specific parameter from an object array

I'm currently storing data as objects inside a array in the following way:
let data = [];
module.exports.init = function() {
database.pool.query("SELECT * FROM data", (error, rows) => {
if (error) {
logUtil.log.error(`Loading failed: ${ error.message }`);
}
else {
rows.forEach((row) => data.push({dimension: row.dimension, x: row.x, y: row.y, z: row.z}));
logUtil.log.info(data);
}
});
};
data will hold the following now: [{ dimension: 2, x: -973.097, y: -133.411, z: 38.2531 }, { dimension: 3, x: -116.746, y: -48.414, z: 17.226 }, { dimension: 2, x: -946.746, y: -128.411, z: 37.786 }, { dimension: 2, x: -814.093, y: -106.724, z: 37.589 }]
Now I'm trying to receive a random object from this array storing a specific dimension parameter.
For example I want to return a random object storing the dimension: 2
I've tried to filter the array using something like:
let result = jsObjects.filter(data => {
return data.dimension === 2
})
then return a random object from the result.
Question: How could I receive this random object in the best way?
You can do it in two steps.
Get all record which satisfy criteria like dimension === 2
let resultArr = jsObjects.filter(data => {
return data.dimension === 2
})
Get random object from result.
var randomElement = resultArr[Math.floor(Math.random() * resultArr.length)];
var arr = [{ dimension: 2, x: -973.097, y: -133.411, z: 38.2531 }, { dimension: 3, x: -116.746, y: -48.414, z: 17.226 }, { dimension: 2, x: -946.746, y: -128.411, z: 37.786 }, { dimension: 2, x: -814.093, y: -106.724, z: 37.589 }]
//Filter out with specific criteria
let resultArr = arr.filter(data => {
return data.dimension === 2
})
//Get random element
var randomElement = resultArr[Math.floor(Math.random() * resultArr.length)];
console.log(randomElement)
You could use Math.random() and in the range of 0 to length of array.
let result = jsObjects.filter(data => {
return data.dimension === 2
})
let randomObj = result[Math.floor(Math.random() * result.length)]

Is there an easy way to merge objects and sum their int properties instead of overriding in javascript es2018? [duplicate]

This question already has answers here:
Merge two javascript objects adding values of common properties
(4 answers)
Closed 1 year ago.
Alright, so this is what I have in mind.
I have two example objects
let obj1 = { x: 60 };
let obj2 = { x: 9 };
I want to merge these objects in a way that their integer properties will combine as well, and not override each other, so the end result will be
let obj3 = { x: 69 };
So I've looked into Object.assign, but this function only merges properties in a way that it favors the properties on the first object if the other objects have a property with the same name, and doesn't sum integer properties.
I can, of course, just make a function that loops over each object's properties and creates a new object with the summed properties, but that'd be longer and I wanted to know if there's already a function that does it easily like the way Object.assign acts.
Thank you.
If you want to use Lodash library you could do this with mergeWith method.
let obj1 = { x: 60, b: "foo", c: {y: 3, x: 'bar'} };
let obj2 = { x: 9, a: 'bar', c: {y: 4} };
const sum = _.mergeWith(obj1, obj2, (a, b) => {
if (_.every([a, b], _.isNumber)) return a + b;
})
console.log(sum)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.js"></script>
You could reduce the objects by taking an arbitrary count of objects and reduce the key/value pairs by respecting nested objects.
const
add = (...a) => a // take all parameters
.map(Object.entries) // get entries
.reduce((a, b) => [...a, ...b], []) // flat entries
.reduce((o, [k, v]) => {
o[k] = v && typeof v === 'object' // assign if object
? add(o[k] || {}, v) // result of recursive call
: (o[k] || 0) + v; // or sum the value
return o;
}, {});
let obj1 = { x: 60, y: { z: 3 } },
obj2 = { x: 9, y: { z: 1, a: 32 } },
obj3 = { y: { a: 10 } },
result = add(obj1, obj2, obj3);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Like this?
let obj1 = { x: 60 };
let obj2 = { x: 9 };
let obj3 = [obj1, obj2].reduce((s,a)=>{return {x: s.x+a.x}}, {x:0});
console.log(obj3);
// or more shortener:
obj3 = {x:[obj1, obj2].reduce((s,a)=> s + a.x,0)};
console.log(obj3);
// for arbitrary properties, not just x:
keys = Object.keys(obj1),
obj3 = [obj1, obj2].reduce(function (r, o) {
keys.forEach(function (k) {
r[k] += o[k];
});
return r;
}, keys.reduce(function (r, k) {
r[k] = 0;
return r;
}, Object.create(null)));
console.log(obj3);

Flatten different properties into single array

I have a javascript object literal as follows.
data: {
prop1ByZones:[{zone: "Zone1", x: 1}, {zone: "Zone2", x: 5}],
prop2ByZones:[{zone: "Zone1", y: "1302.5"}],
prop3ByZones:[{zone: "Zone2", z: 2}]
}
the output should be like -
output: [{zone: "Zone1", x: 1, y: "1302.5", z: 0}, {zone: "Zone2", x: 5, y: 0, z: 2}]
I can do it in trivial way like first add prop1ByZones to output and then loop through prop2ByZones and prop3ByZones and check for existing zone. if the zone is there then update it else add it.
I just wanted to check if there is any elegant way of doing it. Please let me know.
One possible approach:
var data = { prop1ByZones:[{zone: "Zone1", x: 1}, {zone: "Zone2", x: 5}], prop2ByZones:[{zone: "Zone1", y: "1302.5"}], prop3ByZones: [{zone: "Zone2", z: 2}] }
var arr = [...data.prop1ByZones, ...data.prop2ByZones, ...data.prop3ByZones]
var resp = arr.reduce((acc, { zone, x, y, z }) => {
var prev = acc.find(x => zone == x.zone);
if(prev) {
prev.x = x ? x : prev.x,
prev.y = y ? y : prev.y,
prev.z = z ? z : prev.z
return acc;
}
return acc.concat({zone: zone, x: x ? x : 0, y: y ? y : 0, z: z ? z : 0});
}, []);
console.log(resp)
.as-console-wrapper { max-height: 100% !important; top: 0; }
Here's a way to do this (using lodash for iterating), you could optimize the conditionals a bit to be ternaries etc, but this would give you your output:
const data = {
prop1ByZones:[{zone: "Zone1", x: 1}, {zone: "Zone2", x: 5}],
prop2ByZones:[{zone: "Zone1", y: "1302.5"}],
prop3ByZones:[{zone: "Zone2", z: 2}]
};
let list = {};
_.each(data, (d, i) => {
_.each(d, (e) => {
const zone = e.zone;
if (!list[zone]) {
list[zone] = zone;
list[zone] = {};
}
if (!list[zone].x) {
list[zone].x = e.x || 0;
}
if (!list[zone].y) {
list[zone].y = e.y || 0;
}
if (!list[zone].z) {
list[zone].z = e.z || 0;
}
});
});
// put everything in an array
let result = [];
_.each(list, (obj, k) => {
result.push({
zone: k,
x: obj.x,
y: obj.y,
z: obj.z
});
});
console.log(result);
You can get the Object.values(), flatten to a single array by spreading into Array.concat(), and then reduce the array to a Map, and spread the Map.values() iterator back to array:
const data = {
prop1ByZones:[{zone: "Zone1", x: 1}, {zone: "Zone2", x: 5}],
prop2ByZones:[{zone: "Zone1", y: "1302.5"}],
prop3ByZones:[{zone: "Zone2", z: 2}]
}
const result = [... // spread the map values iterator (see end) back to an array
[].concat(...Object.values(data)) // get the object's values and flatten to a single array
.reduce( // reduce the array to a Map
// add the zone key to the map, and include the previous and current item
(r, o) => r.set(o.zone, { ...(r.get(o.zone) || {}), ...o }),
new Map()
).values()] // get the map values iterator
console.log(result)

Changing the order of the Object keys....

var addObjectResponse = [{
'DateTimeTaken': '/Date(1301494335000-0400)/',
'Weight': 100909.090909091,
'Height': 182.88,
'SPO2': '222.00000',
'BloodPressureSystolic': 120,
'BloodPressureDiastolic': 80,
'BloodPressurePosition': 'Standing',
'VitalSite': 'Popliteal',
'Laterality': 'Right',
'CuffSize': 'XL',
'HeartRate': 111,
'HeartRateRegularity': 'Regular',
'Resprate': 111,
'Temperature': 36.6666666666667,
'TemperatureMethod': 'Oral',
'HeadCircumference': '',
}];
This is a sample object which i am getting from back end, now i want to change the order of the object. I don't want to sort by name or size... i just want to manually change the order...
If you create a new object from the first object (as the current accepted answer suggests) you will always need to know all of the properties in your object (a maintenance nightmare).
Use Object.assign() instead.
*This works in modern browsers -- not in IE or Edge <12.
const addObjectResponse = {
'DateTimeTaken': '/Date(1301494335000-0400)/',
'Weight': 100909.090909091,
'Height': 182.88,
'SPO2': '222.00000',
'BloodPressureSystolic': 120,
'BloodPressureDiastolic': 80,
'BloodPressurePosition': 'Standing',
'VitalSite': 'Popliteal',
'Laterality': 'Right',
'CuffSize': 'XL',
'HeartRate': 111, // <-----
'HeartRateRegularity': 'Regular', // <-----
'Resprate': 111,
'Temperature': 36.6666666666667,
'TemperatureMethod': 'Oral',
'HeadCircumference': '',
};
// Create an object which will serve as the order template
const objectOrder = {
'HeartRate': null,
'HeartRateRegularity': null,
}
const addObjectResource = Object.assign(objectOrder, addObjectResponse);
The two items you wanted to be ordered are in order, and the remaining properties are below them.
Now your object will look like this:
{
'HeartRate': 111, // <-----
'HeartRateRegularity': 'Regular', // <-----
'DateTimeTaken': '/Date(1301494335000-0400)/',
'Weight': 100909.090909091,
'Height': 182.88,
'SPO2': '222.00000',
'BloodPressureSystolic': 120,
'BloodPressureDiastolic': 80,
'BloodPressurePosition': 'Standing',
'VitalSite': 'Popliteal',
'Laterality': 'Right',
'CuffSize': 'XL',
'Resprate': 111,
'Temperature': 36.6666666666667,
'TemperatureMethod': 'Oral',
'HeadCircumference': '',
}
I wrote this small algorithm which allows to move keys, it's like jQuery .insertAfter() method. You have to provide:
//currentKey: the key you want to move
//afterKey: position to move-after the currentKey, null or '' if it must be in position [0]
//obj: object
function moveObjectElement(currentKey, afterKey, obj) {
var result = {};
var val = obj[currentKey];
delete obj[currentKey];
var next = -1;
var i = 0;
if(typeof afterKey == 'undefined' || afterKey == null) afterKey = '';
$.each(obj, function(k, v) {
if((afterKey == '' && i == 0) || next == 1) {
result[currentKey] = val;
next = 0;
}
if(k == afterKey) { next = 1; }
result[k] = v;
++i;
});
if(next == 1) {
result[currentKey] = val;
}
if(next !== -1) return result; else return obj;
}
Example:
var el = {a: 1, b: 3, c:8, d:2 }
el = moveObjectElement('d', '', el); // {d,a,b,c}
el = moveObjectElement('b', 'd', el); // {d,b,a,c}
You can't order JavaScript object key/value pairs. It's stored in its own internal format, so you should never rely on the order of that. In JS, everything is an Object, even an Array. So sometimes you can introduce bugs when using array notation and object notation together (for x in var)
I like the approved answer by Chamika Sandamal. Here's a simple function that uses their same logic with a little be of freedom to change the order as you need it.
function preferredOrder(obj, order) {
var newObject = {};
for(var i = 0; i < order.length; i++) {
if(obj.hasOwnProperty(order[i])) {
newObject[order[i]] = obj[order[i]];
}
}
return newObject;
}
You give it an object, and an array of the key names you want, and returns a new object of those properties arranged in that order.
var data = {
c: 50,
a: 25,
d: 10,
b: 30
};
data = preferredOrder(data, [
"a",
"b",
"c",
"d"
]);
console.log(data);
/*
data = {
a: 25,
b: 30,
c: 50,
d: 10
}
*/
I'm copying and pasting from a big JSON object into a CMS and a little bit of re-organizing of the source JSON into the same order as the fields in the CMS has saved my sanity.
2020 Update
The answer below was correct at time of writing in 2011. However, since ES6, enumeration order has been specified as part of the language. Here's a nice article summarising this: https://2ality.com/2015/10/property-traversal-order-es6.html
Original answer
Properties of an object in JavaScript do not have an order. There may appear to be an order in some browsers but the ECMAScript specification defines object property enumeration order as being implementation-specific so you should not assume one browser's behaviour will be the same as another's. Chrome, for example, does not use the same ordering as some other browsers: see this lengthy bug report for at least as much discussion of this issue as you could possibly want.
If you need a specific order, use an array, or two arrays (one for keys and one for values).
You can use Object.keys():
var obj = {
a: 1,
b: 2,
c: 4
};
var new_obj = {};
Object.keys(obj)
.sort(function(a, b) {
/** Insert your custom sorting function here */
return a - b;
})
.forEach(function(key) {
new_obj[key] = obj[key];
});
obj = new_obj;
if you want to manually reorder. simply create new object and assign values using old object.
var newObject= [{
'DateTimeTaken': addObjectResponse.DateTimeTaken,
'Weight': addObjectResponse.Weight,
'Height': addObjectResponse.Height,
'SPO2': addObjectResponse.SPO2
}];
If you do not want to create a new object, you can use the following code snippet.
function orderKey(obj, keyOrder) {
keyOrder.forEach((k) => {
const v = obj[k]
delete obj[k]
obj[k] = v
})
}
here is a codepen: https://codepen.io/zhangyanwei/pen/QJeRxB
I think that's not possible in JavaScript.
You can create an array which will contain the field names in your order and you can iterate through this array and fetch the fields from the actual object.
Just refer to the object keys in the order that you like:
aKeys = [
addObjectResponse[0].DateTimeTaken,
addObjectResponse[0].Weight,
addObjectResponse[0].Height,
...etc...
]
I wrote a quick function in TypeScript that takes 2 arguments. The first is the array of objects you want to change the keys of, the second is an array of strings that represent the order of keys you'd like returned.
type GenericObject = Record<string, any> | null;
const order:Function = (data: Array<GenericObject>, order: Array<string>): Array<GenericObject> => {
return data.map((node) => {
return order.reduce((runningValue, currentValue) => {
return Object.assign(runningValue, { [currentValue]: node?.[currentValue]});
}, {});
});
};
And here is an example of calling it:
const data: Array<GenericObject> = [
{ b: 1, a: 2},
{ b: 3, a: 4},
];
const orderIWant: Array<string> = ['a', 'b'];
const ordered: Array<GenericObject> = order(data, orderIWant);
console.log(ordered);
// [ { a: 2, b: 1 }, { a: 4, b: 3 } ]
function orderKeys(obj, keys){
const newObj = {};
for(let key of keys){
newObj[key] = obj[key];
}
return newObj;
}

Categories