I have a scenario, were need to compare treeObject1 and treeObject2 to determine the exact difference at property level and find the parent of modified node.
In below provided objects, I need to get output as color blue. Since the difference is at otherObj2.
treeObject1 = {
color: "red",
value: 10,
otherObj: {
color: "blue",
otherObj2: {
otherColor: "blue",
otherValue: 20,
}
}
}
treeObject2 = {
color: "red",
value: 10,
otherObj: {
color: "blue",
otherObj2: {
otherColor: "Green",
otherValue: 20,
}
}
}
If you want the key "otherObj" as well let me know, that can easily be added. Otherwise here is a working version of what you were looking for.
This uses a combination of Object.keys and every
treeObject1 = {
color: "red",
value: 10,
otherObj: {
color: "blue",
otherObj2: {
otherColor: "blue",
otherValue: 20,
}
}
}
treeObject2 = {
color: "red",
value: 10,
otherObj: {
color: "blue",
otherObj2: {
otherColor: "Green",
otherValue: 20,
}
}
}
const findParentNode = (obj1, obj2, parent = null) => {
if(parent === null) parent = obj2;
//since the structures are the same we only get keys from the first object
const keys = Object.keys(obj1);
let result = null;
//iterate through every key
keys.every(key=>{
//if it's an object... then we recall findParentNode (recursive)
if(obj1[key] instanceof Object){
result = findParentNode(obj1[key], obj2[key], obj2);
//If result from findParentNode is not null then a difference was found.
//Return false to stop the every method.
if(result !== null) return false;
}else if(obj1[key] !== obj2[key]){
//If the objects are different we found a difference
//Set the parent as the difference
result = parent;
return false;
}
//return true to keep on looping
return true;
});
//return the result
return result;
}
console.log(findParentNode(treeObject1, treeObject2));
** note that the above snippet will return "null" if nothing was found. **
You could use a nested approach for objects and by checking the values.
function getDiffParents(object1, object2, parent = {}) {
return Object.assign(...Object.entries(object1).map(([k, v]) => v && typeof v === 'object'
? getDiffParents(v, object2[k], object1)
: v === object2[k]
? {}
: parent
));
}
var treeObject1 = { color: "red", value: 10, otherObj: { color: "blue", otherObj2: { otherColor: "blue", otherValue: 20 } } },
treeObject2 = { color: "red", value: 10, otherObj: { color: "blue", otherObj2: { otherColor: "Green", otherValue: 20 } } };
console.log(getDiffParents(treeObject1, treeObject2));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Related
I want to ask for help with the problem. I have an existing deep Javascript object from which I want to dynamically generate multiple versions.
I have a method that has 2 parameters.
first: the object from which I want to generate new ones,
second: a number or an array of numbers
for example:
let myObj = {
brown: {
50: '#f9f8f2',
100: '#f3f0e6',
},
singleProp: '#e6b01e',
propLvl1: {
color: '#32a852',
sub1: {
color: '#44eff2',
sub2: {
color: '#f2448d'
},
},
},
};
myFunction(myObject, [10, 30]);
my goal would be:
MY-10-brown: {
50: '#(DYNAMICVALUE)f9f8f2',
100: '#(DYNAMICVALUE)f3f0e6',
},
MY-10-singleProp: '#(DYNAMICVALUE)e6b01e',
MY-10-propLvl1: {
color: '#(DYNAMICVALUE)32a852',
sub1: {
color: '#(DYNAMICVALUE)44eff2',
sub2: {
color: '#(DYNAMICVALUE)f2448d'
},
},
}
MY-30-brown: {
50: '#(DYNAMICVALUE)f9f8f2',
100: '#(DYNAMICVALUE)f3f0e6',
},
MY-30-singleProp: '#(DYNAMICVALUE)e6b01e',
MY-30-propLvl1: {
color: '#(DYNAMICVALUE)32a852',
sub1: {
color: '#(DYNAMICVALUE)44eff2',
sub2: {
color: '#(DYNAMICVALUE)f2448d'
},
},
}
So far I have reached him:
export default function generateObjects(obj, numbers) {
let newObj = {};
for (let q = 0; q < transparentValue.length; q += 1) {
let Obj = doTheJob(obj, transparentValue[q]);
Object.assign(newObj, Obj);
}
return newObj;
}
function doTheJob(obj, number) {
const newObj = {};
let newKey = '';
Object.keys(obj).forEach(function (key) {
let trim = `${obj[key]}`.substring(1);
let newValue = `#${anotherObject[number]}${trim}`;
if (typeof obj[key] === 'object') {
newKey = `MY-${number}-${key}`;
newObj[newKey] = obj[key];
generateNewObj(newObj[newKey], number);
return;
}
if (typeof obj[key] === 'string') {
newObj[key] = newValue;
}
});
return newObj;
}
You could create new properties for the first level of the object and take copies of data.
const
copy = value => typeof value === 'object'
? Object.fromEntries(Object.entries(value).map(([k, v]) => [k, copy(v)]))
: typeof value === 'string'
? value.replace('#', '#DYNAMICVALUE')
: value
create = (object, values, header) => Object.fromEntries(Object
.entries(object)
.reduce((r, [k, v]) => [...r, ...values.map(i => [[header, i, k].join('-'), copy(v)])], [])
),
myObj = { brown: { 50: '#f9f8f2', 100: '#f3f0e6' }, singleProp: '#e6b01e', propLvl1: { color: '#32a852', sub1: { color: '#44eff2', sub2: { color: '#f2448d' } } } };
console.log(create(myObj, [10, 30], 'my'));
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can:
Create a new object
Loop through each number in the array
Inside the loop, loop through each property in the object and assign the value of the property to the new object's modified property ("MY-"+num+"-"+prop).
let myObj = {
brown: {
50: '#f9f8f2',
100: '#f3f0e6',
},
singleProp: '#e6b01e',
propLvl1: {
color: '#32a852',
sub1: {
color: '#44eff2',
sub2: {
color: '#f2448d'
},
},
},
};
function process(obj, numArr){
const newObj = {};
for(const num of numArr){
for(const prop in obj){
newObj['MY-'+num+'-'+prop] = obj[prop];
}
}
return newObj;
}
console.log(JSON.stringify(process(myObj, [10, 30]), 0, 2))
.as-console-wrapper{max-height:100%!important;top:0}
I have this array with objects that look like this
{
n: 15,
color: "red"
}
I am trying to sort it with the below function
async insertionSort() {
let len = this.array.length;
let value;
let i;
let j;
//let current;
// let arr = this.array;
for (i = 0; i < len; i++) {
value = this.array[i].n;
//current = this.array[i];
for (j = i - 1; j > -1 && this.array[j].n > value; j++) {
//arr[j + 1] = arr[j];
// HF.arraySwap(this.array, this.array[j + 1], this.array[j]);
this.array[j + 1] = this.array[j];
}
// arr[j + 1] = value;
HF.arraySwap(this.array, this.array[j + 1], this.array[i]);
await HF.sleep();
}
}
** I cant use array.sort(...) because i am trying to make a visualization of the algorithm, i am using objects in order to change the color of the bars i am rendering on the screen **
When i hit the second for loop i get an error of "Cannot read property 'n' of undefined", when i run it with just numbers it works fine but when i try it with objects it gives the error.I know now i am running out of the array, is there a way i can overcome this and still sort the array of objects? Also, i am using VueJS to display all of this
Try against this.array[i].n write this.array[i][n]
And against this.array[j].n write this.array[j][n]
On the first iteration i=0, you start second loop with value j=i-1 which is -1. Array doesn't contain item with index -1: array[-1] is undefined. As soon as JavaScript can compare variables of different types it works with numbers because comparison of number and undefined won't trigger an error
By the way you can use Array.proototype.sort method, it will look like:
console.log(myArr.sort((a,b) => a.n - b.n))
<script>
const myArr = [
{ n: 1, color: "red" },
{ n: 44, color: "orange" },
{ n: 13, color: "yellow" },
{ n: 8, color: "green" },
{ n: 2, color: "blue" }
];
</script>
Is there any reason why not use sort method like this?:
const arr = [
{ n: 10, color: "red" },
{ n: 20, color: "yellow" },
{ n: 15, color: "black" },
{ n: 7, color: "white" },
{ n: 23, color: "blue" }
];
const ascSorted = arr.sort((a, b) => a.n - b.n);
const descSorted = arr.sort((a, b) => b.n - a.n);
console.log(ascSorted);
// [
// { n: 7, color: "white" },
// { n: 10, color: "red" },
// { n: 15, color: "black" },
// { n: 20, color: "yellow" },
// { n: 23, color: "blue" }
// ];
So suppose my array looks like this:
let langArr = [
["python", "blue"]
,["python", "blue"]
,["c++", "red"]
,["java", "yellow"]
,["javascript", "lime"]
,["shell", "green"]
,["c++", "red"]
];
what I want is something like this:
{
python: {
count: 2
color: "blue"
}
c++: {
count: 2
color: "red"
}
java: {
count: 1
color: "yellow"
}
and so on...
}
I tried reduce method like this:
let langCount = langArr.reduce((lang, [name, color]) => {
lang[name] = (lang[name] || 0) + 1;
lang[color] = 'color';
return lang;
}, {});
console.log(langCount);
but I get this output:
{
python: 2
blue: "color"
c++: 2
red: "color"
java: 1
yellow: "color"
and so on...
}
You need an object for each language.
This approach takes an object as default value if lang[name] is falsy, like undefined.
The pattern
variable = variable || value;
works with a logical OR ||:
if variable has a truthy value, take this value,
if variable has a falsy value, take value instead.
let langArr = [["python", "blue"], ["python", "blue"], ["c++", "red"], ["java", "yellow"], ["javascript", "lime"], ["shell", "green"], ["c++", "red"]],
langCount = langArr.reduce((lang, [name, color]) => {
lang[name] = lang[name] || { count: 0, color };
lang[name].count++;
return lang;
}, {});
console.log(langCount);
You can use this:
array.reduce((acc, current) => {
if(!acc.hasOwnProperty(current[0])){
acc[current[0]] = {count: 0, color: current[1]};
}
acc[current[0]].count += 1;
return acc;
}, {});
I'm learning ES6 from a tutorial and while playing with the code, I found something that I didn't understand. The code below outputs '3'.
var primaryColors = [
{ color: 'red' },
{ color: 'yellow' },
{ color: 'blue' },
];
var newColors = [];
primaryColors.reduce(function(color, primaryColor){
return newColors.push(primaryColor.color);
}, []);
Why is the return statement returning the no of data in the "stack"?
Why is reduce function outputting no of items in an array?
As Nenad Vracar said, because push returns the number of items in the array, and reduce returns the last value the callback returned.
reduce is not the right tool for this job. map is:
var newColors = primaryColors.map(function(primaryColor) {
return primaryColor.color;
});
var primaryColors = [
{ color: 'red' },
{ color: 'yellow' },
{ color: 'blue' },
];
var newColors = primaryColors.map(function(primaryColor) {
return primaryColor.color;
});
console.log(newColors);
or with an ES2015 arrow function:
var newColors = primaryColors.map(primaryColor => primaryColor.color);
var primaryColors = [
{ color: 'red' },
{ color: 'yellow' },
{ color: 'blue' },
];
var newColors = primaryColors.map(primaryColor => primaryColor.color);
console.log(newColors);
and if we're doing ES2015, we can throw in destructuring:
var newColors = primaryColors.map(({color}) => color);
var primaryColors = [
{ color: 'red' },
{ color: 'yellow' },
{ color: 'blue' },
];
var newColors = primaryColors.map(({color}) => color);
console.log(newColors);
Let's have an object with some default settings:
var defaults = {
id: '',
x: 0,
y: 0,
width: 20,
height: 20,
styles: {
color: '#ffffff',
background_color: '#000000'
},
points: []
}
Then, we make our own object, which initially extends the default settings, and makes some changes:
var newObject = {
id: '1', // changed
x: 10, // changed
y: 10, // changed
width: 20,
height: 20,
styles: {
color: '#ffffff',
background_color: '#333333' // changed
},
points: [1, 2, 3]
}
Finally, we need an object, which contains only the values that changed from the default settings, like this:
var subtracted = {
id: '1',
x: 10,
y: 10,
styles: {
background_color: '#333333'
},
points: [1, 2, 3]
}
The algorithm needs to be recursive, there can be objects within objects. Here is what I have so far:
function subtract(a, b) {
var r = {};
// For each property of 'b'
// if it's different than the corresponding property of 'a'
// place it in 'r'
for (var key in b) {
if (typeof(b[key]) == 'object') {
if (!a[key]) a[key] = {};
r[key] = subtract(a[key], b[key]);
} else {
if (b[key] != a[key]) {
r[key] = a[key];
}
}
}
return r;
}
However, the recursion is not working for arrays, so "points" turns out as an empty object! typeof() detects it as an object and fails to clone its properties, somehow.
https://jsfiddle.net/gd8q1u18/1/
Your code is working. though there is one edit I made in it to make it recursive as well.
var defaults = {
id: '',
x: 0,
y: 0,
width: 20,
height: 20,
styles: {
color: '#ffffff',
background_color: '#000000'
},
points: []
}
var newObject = {
id: '1', // changed
x: 10, // changed
y: 10, // changed
width: 20,
height: 20,
styles: {
color: '#ffffff',
background_color: '#333333' // changed
},
points: [0, 1, 2] // changed
}
var subtracted = {
id: '1',
x: 10,
y: 10,
styles: {
background_color: '#333333'
}
}
function isSame(a, b) {
if (a.length != b.length) return false;
if (a.filter(function(i) {
return a.indexOf(i) < 0;
}).length > 0)
return false;
if (b.filter(function(i) {
return a.indexOf(i) < 0;
}).length > 0)
return false;
return true;
};
function subtract(a, b) {
var r = {};
// For each property of 'b'
// if it's different than the corresponding property of 'a'
// place it in 'r'
for (var key in b) {
if (Array.isArray(b[key])) {
if (!a[key]) a[key] = [];
if (!isSame(a[key], b[key]))
r[key] = a[key];
} else if (typeof(b[key]) == 'object') {
if (!a[key]) a[key] = {};
r[key] = subtract(a[key], b[key]);
} else {
if (b[key] != a[key]) {
r[key] = a[key];
}
}
}
return r;
}
console.log(subtract(newObject, defaults));
UPDATE: another approach a little bit more toward recursion. It runs through modifications so newObject can disregard some fields. It works with primitives too.
const equalArrays = (arr1, arr2) => arr1.length === arr2.length && arr1.every((element, index) => element === arr2[index])
// notice that equalArrays matters the order of the arrays' elements.
// If order doesn't matter, consider sorting both arrays first
const isObject = (obj) => obj instanceof Object && !(obj instanceof Array)
// notice that arrays are instance of Objects too
// an unwary consumer might mix arrays and objects with unpredictable results
const isArray = (arr) => arr instanceof Array
const getDifferences = (original, modified) => {
const areArrays = isArray(original) && isArray(modified)
const areObjects = isObject(original) && isObject(modified)
if (areObjects) {
let result = {}
for (const key of Object.keys(modified)) {
const diff = getDifferences(original[key], modified[key])
if (diff) result[key] = diff
}
return !!Object.keys(result).length && result
}
else if (areArrays && !equalArrays(original, modified)) return modified
else if (original !== modified) return modified
}
// notice that some variables and functions are there for readability and might be inlined
let defaults = {
id: '',
x: 0,
y: 0,
width: 20,
height: 20,
styles: {
color: '#ffffff',
background_color: '#000000'
},
points: []
}
let newObject = {
id: '1', // changed
x: 10, // changed
y: 10, // changed
width: 20,
height: 20,
styles: {
color: '#ffffff',
background_color: '#333333' // changed
},
points: [0, 1, 2] // changed
}
console.log(getDifferences(defaults, newObject))
I would take into account that some unwary consumer might mix arrays and objects.
const equalArrays = (arr1, arr2) => arr1.length === arr2.length && arr1.every((element, index) => element === arr2[index])
// notice that equalArrays matters the order of the arrays' elements.
// If order doesn't matter, consider sorting both arrays first
const isObject = (obj) => obj instanceof Object && !(obj instanceof Array)
// notice that arrays are instance of Objects too
// an unwary consumer might mix arrays and objects with unpredictable results
const isArray = (arr) => arr instanceof Array
const getDifferences = (obj1, obj2) => {
let obj3 = {}
for (const key of Object.keys(obj1)) {
const val1 = obj1[key]
const val2 = obj2[key]
const areArrays = isArray(val1) && isArray(val2)
const areObjects = isObject(val1) && isObject(val2)
if (areObjects) {
const diff = getDifferences(val1, val2)
if (diff) obj3[key] = diff
}
else if (areArrays && !equalArrays(val1, val2)) obj3[key] = val2
else if (val1 !== val2) obj3[key] = val2
}
return !!Object.keys(obj3).length && obj3
}
// notice that some variables and functions are there for readability and might be inlined
let defaults = {
id: '',
x: 0,
y: 0,
width: 20,
height: 20,
styles: {
color: '#ffffff',
background_color: '#000000'
},
points: []
}
let newObject = {
id: '1', // changed
x: 10, // changed
y: 10, // changed
width: 20,
height: 20,
styles: {
color: '#ffffff',
background_color: '#333333' // changed
},
points: [0, 1, 2] // changed
}
console.log(getDifferences(defaults, newObject))