I have two identical objects with me
let a = {
title : "developer”,
startDate:{ month :’jan’}
}
let b = {
title :{
value: ""
} ,
startDate :{month:{value:””}}
}
i need to merge dynamically these two to get object like below
let c = {
title :{
value: "developer"
} ,
startDate:{
month:{ value:” jan”}}
}
You don't require object b because it's just a replica of object a with extra 'value' property.
You can traverse the complete a object and then deep copy the value in the b object.
I wrote a recursive method for this where you can traverse to the last level of the object and copy the value in another object.
function mergeObj(sourceObj, newObj) {
Object.keys(sourceObj).forEach(key => {
if (sourceObj[key] && typeof sourceObj[key] === 'object') {
newObj[key] = {};
mergeObj(sourceObj[key], newObj[key]);
} else {
// updating properties
newObj[key] = {};
newObj[key]['value'] = sourceObj[key];
}
});
}
let a = {
title : "developer",
startDate:{ month :'jan'}
};
let b = {};
mergeObj(a,b);
console.log(b);
You probably need to start by making both object have the same structure, and then run the deep merge. lodash's merge can help you with it
const newA = Object.entries(a).reduce((newObject, [key, value]) => ({
...newObject,
[key]: { value },
}, {}))
// newA looks now like
//
// {
// title: {
// value: "developer
// }
// }
let c = _.merge(a, b); // lodash merge for deep merge. Otherwise write your own
Here is a workaround for your problem:
let a = {
title : "developer",
startDate:{ month :'jan'}
}
let b = {
title :{
value: ''
} ,
startDate :{month:{value:''}}
}
var c = {};
c.startDate = {};
c.title = {};
c.startDate.month = {};
c.startDate.month.value = a.startDate.month;
c.title.value = a.title;
console.log("Merged object",c);
You can just implement a function that does this for you. Given your example:
let a = {
title: "developer",
startDate: { month: "jan" }
};
let b = {
title: {
value: ""
},
startDate: { month: { value: "" }}
};
You can use this to get the values:
const mergeObject = (a, b) => {
b.title.value = a.title;
b.startDate.month.value = a.startDate.month;
return b;
};
If you call now say let c = mergeObject(a, b) c will be
let c = {
title: {
value: "developer"
},
startDate: {
month: { value: "jan" }}
}
Of course this function can be modified to reflect your exact needs.
Related
I would like to make a deep copy (break references) without turning date objects into strings, how would I do that?
let a = [{
date: new Date()
}, {
name: 'John'
}];
// let b = a.slice(); // This copies the object reference, which is wrong (we want a COPY of the obejcts)
// let b = [...a]; // This copies the object reference, which is wrong (we want a COPY of the obejcts)
// let b = JSON.parse(JSON.stringify(a)); // This makes the date a string, which is wrong (we want date objects, not strings)
let b = a.slice();
a[1].name = 'PETER';
console.log(a);
// [ { date: 2020-06-08T09:10:32.530Z }, { name: 'PETER' } ]
console.log(b);
// [ { date: 2020-06-08T09:10:32.530Z }, { name: 'PETER' } ]
Here is a good answer on Javascript Deep Copying: Copy array by value
This will deep copy the object, let me know if this resolves your issue:
let a = [{ date: new Date() }, { name: 'John'}];
let b = a.map(k=>({...k}));
a[1]="PETER";
console.log(a);
console.log(b);
Here is a utility to deep copy the objects. Hope this helps
const deepCopy = (objectToBeCloned) => {
let resultObj, value, key
if (typeof objectToBeCloned !== "object" || objectToBeCloned === null) {
return objectToBeCloned
}
if(typeof objectToBeCloned === "object") {
if(objectToBeCloned.constructor.name !== "Object") {
resultObj = new objectToBeCloned.constructor(objectToBeCloned)
} else {
resultObj = Array.isArray(objectToBeCloned) ? [] : {}
}
}
for (key in objectToBeCloned) {
value = objectToBeCloned[key]
// Recursively copy for nested objects & arrays
resultObj[key] = deepCopy(value)
}
return resultObj
}
let a = [{
date: new Date(),
x: {y:{z:1}},
test: [1,2,3,4],
testArrObj: [{x:1, y:2}]
}, {
name: 'John'
}];
let b = deepCopy(a)
a[1].name = "PETER";
console.log(b);
console.log(a);
console.log(a === b)
console.log(b === b)
Basic deep clone with date + timezone support written in Typescript (remove the types if not required).
export function clone<T>(x: T) {
// JSON to string
var to = (k: string, v: any) => {
// Serialise date with timezone
if (v && v instanceof Date) {
return v.toUTCString()
}
return v
}
// String to JSON
var from = (k: string, v: any) => {
// Restore date
if (v && typeof v == "string" && v.indexOf("Z") > -1 && moment(v).isValid()) {
return new Date(v)
}
return v
}
return JSON.parse(JSON.stringify(x, to), from) as T
}
let a = [{
date: new Date(),
x: {y:{z:1}},
test: [1,2,3,4],
testArrObj: [{x:1, y:2}]
}, {
name: 'John'
}];
let b = clone(a)
a[1].name = "PETER";
console.log(b);
console.log(a);
console.log(a === b)
console.log(b === b)
Note: An improvement would be to remove the moment dependency.
I am aware of options like the spread operator and Object.assign() when it comes to converting an array to an object, however, I am having difficulty figuring out how to format the final object in the format I need.
My original array looks like this:
let propsArray = [ { id: '1' },
{ 'name.first': 'john' },
{ 'name.last': 'smith' } ]
The object I want from this data needs to look like this:
{
"id" : 1,
"name" : {
"first" : "john",
"last" : "smith"
}
}
What I've tried so far, using object.assign(), ends up adding numbers as property keys in the resulting object, which is clearly not what I'm looking for:
let finalObj = Object.assign({}, propsArray);
How can I get the resulting object formatted the way I need here?
You need a deeper look into the object and take the splitted key and reduce them by taking an object with the property or an empty object. At the end assign the value.
var data = [{ id: '1' }, { 'name.first': 'john' }, { 'name.last': 'smith' }],
result = data.reduce((r, o) => {
Object.entries(o).forEach(([k, v]) => {
var keys = k.split('.'),
last = keys.pop();
keys.reduce((q, k) => q[k] = q[k] || {}, r)[last] = v;
});
return r;
}, {});
console.log(result);
Try this:
const data = [
{
"id": "1"
},
{
"name.first": "john"
},
{
"name.last": "smith"
}
]
const result = Object.entries(Object.assign({}, ...data))
.reduce((acc,[k,v])=>{
[first, last] = k.split('.');
if (!last) {
acc[k] = v
} else {
acc[first] = acc[first] || {};
acc[first][last] = v;
}
return acc
}, {})
console.log(result);
I have an object that looks like this:
{house_id+street_id+school_id: {...}, house_id2+street_id2+school_id2: {...}, ...}
So, each key of the object is a combination of a house_id a street_id and a school_id separated by '+' sign.
I want to be able to filter the object given a street_id, so for example, for the given object:
{40+30+20: { name: "john" }, 41+31+20: { name: "eli" } } and the street_id being 30, the returning object would be:
{40+30+20: "john"}
How can I do that filtering?
You can do it in the following way
let obj = {'40+30+20': { name: "john" }, '41+31+20': { name: "eli" } }
let result = Object.keys(obj).filter(e => e.match(/^.*\+30\+.*$/) != null).reduce((a, b) => {
a[b] = obj[b];
return a;
}, {});
console.log(result);
You can do something like this:
var data = {
'40+30+20': {
name: "john"
},
'41+31+20': {
name: "eli"
}
};
var search = '30';
var r = Object.keys(data).filter(function(key) {
return key.split('+').some(function(p) {
return p === search;
});
}).map(function(key) {
var o = {};
o[key] = data[key];
return o;
});
console.log(r);
Try with the filter function and a regular expression:
var myObj = {'40+30+20': { name: "john" }, '41+31+20': { name: "eli" } };
function filterByStreet(obj, streetId) {
var filteredKeys = Object.keys(obj).filter((key) => {
var patt = new RegExp("[^+]+[+]" + streetId + "[+][0-9]+");
return patt.test(key);
});
var outObj = {};
for(filteredKey of filteredKeys) {
outObj[filteredKey] = obj[filteredKey];
}
return outObj;
}
var filteredObj = filterByStreet(myObj, 30);
console.log(filteredObj);
I am unable to find a reasonable solution as I am pulling JSON data from firebase and pulling it from node.js into an html file. I want to sort my data via a property called "value" by not sure how to access a sub-sub-value to sort by in JQuery and am wondering if someone could help lead me in the right direction.
{
key: "a",
{
key: "ab",
{
value: 2
}
key: "ac",
{
value: 0
}
}
},
{
key: "b",
{
key: "bb",
{
value: 1
}
}
},
Output:
[{ac}, {bb}, {ab}]
Both your input and desired output are expressed in an invalid notation (both in JSON and JavaScript syntax), so I'll have to make some assumptions.
You could use this recursive function, which will find all nested value properties, and collect those values together with the names of the parent properties in which they occur. Finally those pairs of data (parent key and value) are sorted:
function collectValues(obj, name) {
return Object.entries(obj).reduce( (acc, [key, value]) => {
return acc.concat(
// recursively look into nested objects:
Object(value) === value ? collectValues(value, key)
// else, when key is 'value', collect that object
: key == 'value' ? [[name, value]]
// otherwise ignore this value
: []
)
}, []);
}
// Sample input
var input = [{
"a": {
"ab": {
value: 2
},
"ac": {
value: 0
}
}
}, {
"b": {
"bb": {
value: 1
}
}
}];
var result = collectValues(input).sort( (a,b) => a[1] - b[1] );
console.log(result);
Mocking up your JSON from the original image:
var data = {
key1: {
"a": { "deliveryshort": "12152017" },
"b": { "deliveryshort": "10122015" },
"c": { "deliveryshort": "11302016" },
"d": { "deliveryshort": "09022014" }
},
key2: {
"a": { "deliveryshort": "10102017" },
"b": { "deliveryshort": "09102017" }
},
};
function parseDate(dateStr) {
var month = "" + dateStr[0] + dateStr[1];
var day = "" + dateStr[2] + dateStr[3];
var year = "" + dateStr[4] + dateStr[5] + dateStr[6] + dateStr[7];
var result = new Date(year, month, day);
return result;
}
function sortBy(data, property, converter) {
var j = new Array();
for (var item in data) {
j.push([item, data[item], converter(data[item][property])]);
}
j.sort(function (a, b) { return a[2] - b[2] });
return j;
}
function sortData(data) {
var d = {};
for (var item in data) {
var sorted = sortBy(data[item], "deliveryshort", function (a) { return parseDate(a); });
/*var normalized = new Array();
for (var i = 0; i < sorted.length; i++) {
var ni = sorted[i];
var key = ni[0];
var obj = ni[1];
normalized[key] = obj;
}*/
d[item] = sorted;
}
console.log(d);
return d;
}
sortData(data);
I am new to java script , so my apologies if this is trivial.
My question is syntactical, if I have an object:
this.state = {
A : {
B: {
C : [
{value : 'bob'},
{value : 'Jim'},
{value : 'luke'},
]
}
}
}
and I have a string location = 'A.B.C[1]' which describes the location of the data I want.
Why can I not just do data = this.state[location].value ?
and is there a simple "JavaScript" way of getting the data using the location string?
any help would be amazing :)
You could split the path and reduce the object.
function getValue(o, path) {
return path.replace(/\[/g, '.').replace(/\]/g, '').split('.').reduce(function (o, k) {
return (o || {})[k];
}, o);
}
var o = { A : { B: { C: [{ value: 'Brenda' }, { value: 'Jim' }, { value: 'Lucy' }] } }};
console.log(getValue(o, 'A.B.C[1]').value); // Jim
console.log(getValue(o, 'A.B.C[0].value')); // Brenda
console.log(getValue(o, 'Z[0].Y.X[42]')); // undefined
For setting a value, you could split the path and reduce the path by walking the given object. If no object exist, create a new property with the name, or an array. Later assign the value.
function setValue(object, path, value) {
var way = path.replace(/\[/g, '.').replace(/\]/g, '').split('.'),
last = way.pop();
way.reduce(function (o, k, i, kk) {
return o[k] = o[k] || (isFinite(i + 1 in kk ? kk[i + 1] : last) ? [] : {});
}, object)[last] = value;
}
var test = {};
setValue(test, "foo.name", "Mr. Foo");
setValue(test, "foo.data[0].bar", 100);
setValue(test, "and.another[2].deep", 20);
console.log(test);