How to merge two objects containing same keys but different values - javascript

I'm wanting to merge two array that carry objects with the same keys, but one object may have a value, but the other object, carrying the same key, may have a null value. I'm wanting to merge the two to try and replace all the null values and have one final object.
I've written the following, but at the moment, I'm only getting undefined as the final result.
const objectOne = [
{
one: null,
two: "Two",
three: null
}
];
const objectTwo = [
{
one: "One",
two: null,
three: "Three"
}
];
const compareObjects = (searchKey, searchValue) =>
objectTwo.map((retrieve) =>
Object.entries(retrieve).forEach(([retrieveKey, retrieveValue]) => {
if (!searchValue) {
objectOne.searchKey = retrieveValue;
console.log(objectOne);
}
})
);
const newObject = objectOne.map((search) =>
Object.entries(search).forEach(([searchKey, searchValue]) =>
compareObjects(searchKey, searchValue)
)
);
console.log(newObject);

Reduce seems more useful in this case than forEach. Reduce will build a new thing from the input and return that.
const objectOne = [
{
one: null,
two: "Two",
three: null
}
];
const objectTwo = [
{
one: "One",
two: null,
three: "Three"
}
];
const merged = objectOne.map((item, idx) => {
return Object.entries(item).reduce((acc, [key, value]) => {
acc[key] = value === null && objectTwo.length > idx ? objectTwo[idx][key] : value;
return acc;
}, {})
});
console.log(merged);

Your mapping of keys/entries is pretty close. It might be easiest to do something like this though:
const objectOnes = [
{
one: null,
two: "Two",
three: null
},
{
a: 123,
b: undefined,
},
];
const objectTwos = [
{
one: "One",
two: null,
three: "Three"
},
{
b: 456,
}
];
function merge(a, b) {
const result = { ...a }; // copy a
for (const key in result) {
// Let's say we want to pick the non-null/undefined value
if (result[key] !== null && result[key] !== undefined) continue;
result[key] = b[key]; // copy from b
}
return result;
}
const merged = objectOnes.map((obj, i) => merge(obj, objectTwos[i]));
console.log(merged);

One method you can use is to create a function that combines/merges the two dictionaries. First, we create our newObject:
const newObject = [
]
Then, we create our function using an O(n^2) approach and call it:
combine(objectOne,objectTwo)
console.log(newObject)
function combine(objectOne, objectTwo){
for (let [key1,value1] of Object.entries(objectOne[0])){
for (let [key2,value2] of Object.entries(objectTwo[0])){
if(key1 == key2){
if(value1 == null){
newObject.push({
key: key1,
value: value2
})
}
else{
newObject.push({
key: key1,
value: value1
})
}
}
}
}
}
This is the following output:
[
{ key: 'one', value: 'One' },
{ key: 'two', value: 'Two' },
{ key: 'three', value: 'Three' }
]

Related

Separating (n) keys from array of objects into a single array with keys names

I need to perform filter in the array of objects to get all the keys. Although, whenever there is a obj inside of that key, I would need to get the key name and concat with the key name from the obj, so for example:
const data = [ id: 5, name: "Something", obj: { lower: True, higher: False } ]
result = ["id", "name", "obj.lower", "obj.higher"]
I could manage to do the above code, but, if there is more objs inside the data, I would need to keep adding a if condition inside of my logic, I would like to know if there is any other way, so it doesn't matter how many objects I have inside the objects, It will concat always.
The code I used from the above mention:
const itemsArray = [
{ id: 1, item: "Item 001", obj: { name: 'Nilton001', message: "Free001", obj2: { test: "test001" } } },
{ id: 2, item: "Item 002", obj: { name: 'Nilton002', message: "Free002", obj2: { test: "test002" } } },
{ id: 3, item: "Item 003", obj: { name: 'Nilton003', message: "Free003", obj2: { test: "test003" } } },
];
const csvData = [
Object.keys(itemsArray[0]),
...itemsArray.map(item => Object.values(item))
].map(e => e.join(",")).join("\n")
// Separating keys
let keys = []
const allKeys = Object.entries(itemsArray[0]);
for (const data of allKeys) {
if (typeof data[1] === "object") {
const gettingObjKeys = Object.keys(data[1]);
const concatingKeys = gettingObjKeys.map((key) => data[0] + "." + key);
keys.push(concatingKeys);
} else {
keys.push(data[0])
}
}
//Flating
const flattingKeys = keys.reduce((acc, val: any) => acc.concat(val), []);
What I would like to achieve, lets suppose I have this array of object:
const data =
[
{ id: 10, obj: {name: "Name1", obj2: {name2: "Name2", test: "Test"}}}
...
]
Final result = ["id", "obj.name", "obj.obj2.name2", "obj.obj2.test"]
OBS: The first obj contains all the keys I need, no need to loop through other to get KEYS.
I would like to achieve, all the keys from the first object of the array, and if there is objects inside of objects, I would like to concat the obj names (obj.obj2key1)
You could map the key or the keys of the nested objects.
const
getKeys = object => Object
.entries(object)
.flatMap(([k, v]) => v && typeof v === 'object'
? getKeys(v).map(s => `${k}.${s}`)
: k
),
getValues = object => Object
.entries(object)
.flatMap(([k, v]) => v && typeof v === 'object'
? getValues(v)
: v
),
data = { id: 1, item: "Item 001", obj: { name: 'Nilton001', message: "Free001", obj2: { test: "test001" } } },
keys = getKeys(data),
values = getValues(data);
console.log(keys);
console.log(values);
.as-console-wrapper { max-height: 100% !important; top: 0; }
something like this
const itemsArray = [
{ id: 1, item: "Item 001", obj: { name: 'Nilton001', message: "Free001", obj2: { test: "test001" } } },
{ id: 2, item: "Item 002", obj: { name: 'Nilton002', message: "Free002", obj2: { test: "test002" } } },
{ id: 3, item: "Item 003", obj: { name: 'Nilton003', message: "Free003", obj2: { test: "test003" } } },
];
const item = itemsArray[0];
const getAllKeys = (obj, prefix=[]) => {
if(typeof obj !== 'object'){
return prefix.join('.')
}
return Object.entries(obj).flatMap(([k, v]) => getAllKeys(v, [...prefix, k]))
}
console.log(getAllKeys(item))
The OP solution can be simplified by accepting a prefix param (the parent key) and a results param (defaulted to [] and passed into the recursion) to do the flattening...
let obj = { key0: 'v0', key1: { innerKey0: 'innerV0', innerInner: { deeplyNested: 'v' } }, key2: { anotherInnerKey: 'innerV' } }
function recursiveKeys(prefix, obj, result=[]) {
let keys = Object.keys(obj);
keys.forEach(key => {
if (typeof obj[key] === 'object')
recursiveKeys(key, obj[key], result);
else
result.push(`${prefix}.${key}`)
});
return result;
}
console.log(recursiveKeys('', obj))
function getKeys(obj) {
return Object.keys((typeof obj === 'object' && obj) || {}).reduce((acc, key) => {
if (obj[key] && typeof obj[key] === 'object') {
const keys = getKeys(obj[key]);
keys.forEach((k) => acc.add(`${key}.${k}`));
} else {
acc.add(key);
}
return acc;
}, new Set());
}
// accumulate the keys in a set (the items of the array may
// have different shapes). All of the possible keys will be
// stored in a set
const s = itemsArray.reduce(
(acc, item) => new Set([...acc, ...getKeys(item)]),
new Set()
);
console.log('Keys => ', Array.from(s));
You can use recursion as follows. Since typeof([1,3,5]) is object, we also have to confirm that value is not an array, !Array.isArray(value):
const obj = { id: 10, obj: {name: "Name1", obj2: {name2: "Name2", test: "Test"}}};
const getKeys = (o,p) => Object.entries(o).flatMap(([key,value]) =>
typeof(value) === 'object' && !Array.isArray(value) ?
getKeys(value, (p?`${p}.`:"") + key) :
(p ? `${p}.`: "") + key
);
console.log( getKeys(obj) );

How to remove duplicates with extra keys from an array

I have two arrays:
const a1=[
{
a:{ name: "first" }
},
{
b:{ name: "second" }
},
{
c:{ name: "third" }
},
{
d:{ name: "fourth" }
}
]
const a2=[
{
a:{ name: "first", shelf: "read" }
},
{
b:{ name: "second", shelf: "current" }
}
]
I need to check if contents of a1 is in a2 and if it exists replace it in a1 (basically shelf name is missing in a1. I need to update that in a1).
My end result should look like:
[
{
a:{ name: "first", shelf: "read" }
},
{
b:{ name: "second", shelf: "current" }
}
{
c:{ name: "third" }
},
{
d:{ name: "fourth" }
}
]
I have tried something like
const x = a1.map( ele => {
a2.map( e => {
if( e.name === ele.name ) return ele
else return ({ ...ele, shelf: 'none' })
})
return ele;
})
But since I don't have access to the inner maps return value I get the original array back. One way I though of doing this was to concatenate both arrays and use reduce the array by checking the shelf name using javascript's reduce method. Can someone help me with a better method.
I would go by first creating an Auxiliary object for "a2" and when I am looping over a1, in each loop I would check the existence of the current key in the auxiliary object.
Having an Auxiliary Object save you unnecessary loops, you only traverse both the arrays only once.
const a1 = [{a: {name: "first"}}, {b: {name: "second"}}, {c: {name: "third"}}, {d: {name: "fourth"}}];
const a2 = [{a: {name: "first", shelf: "read"}}, {b: {name: "second",shelf: "current"}}];
const dict = a2.reduce((acc, ob) => {
const [k, v] = Object.entries(ob)[0];
acc[k] = v;
return acc;
}, {});
const newA1 = a1.map((ob) => {
const [k, v] = Object.entries(ob)[0];
if (dict[k]) {
Object.assign(v, dict[k]);
}
return {[k]: v};
});
console.log(newA1)
This should do the job in a concise way:
a1.map((it)=> {
const key = Object.keys(it)[0];
const a2item = a2.find( (it2) => Object.keys(it2)[0]===key );
if(a2item) {
Object.assign(it[key], a2item[key]);
}
return it;
});
Please note that the elements of the original a1 array are modified.

changing an array of javascript objects with a single key to an array that contains the objects' data

I currently have data in the following format:
var anArray = [
obj1: {
key1: data1
},
obj2: {
key2: data2
},
];
I would like the data to instead be in the following format:
var array2 = [data1, data2];
for some reason, I cannot figure out a concise way to to this. I know it could be done with a forEach loop that iterates over each object and pushes it onto a new array, but I would prefer to be more elegant (and shorter if possible) than that.
const anArray = {
obj1: {
key1: "A"
},
obj2: {
key2: "B"
},
};
const result = Object.keys(anArray).map(key => {
const obj = anArray[key];
return Object.keys(obj).map(key => obj[key])[0];
});
console.log(result);
Given that anArray is actually properly structured to be valid, then you could do the following:
Note that in this case anArray isn't an actual array but rather a object literal
var anArray = {
obj1: {
key1: "data1"
},
obj2: {
key2: "data2"
},
};
var array2 = []
for(i in anArray){
for(j in anArray[i]){
array2.push(anArray[i][j])
}
}
console.log(array2)
https://jsfiddle.net/wh4r0w5s/
Try with:
const arr1 = [
{key1:'value1'},
{key2:'value2'}
]
const res = arr1.map(obj => {
return Object.keys(obj).map(val => obj[val])
}).reduce((acc,v) => {
return acc.concat(v);
},[]);
console.log(res);
update
But if you have the following form:
var anArray = [
obj1: {
key1: data1
},
obj2: {
key2: data2
},
];
It's better to apply a recursive function, as follow:
const arr1 = [
{
obj1:{key1:'value1',key3:'value3'}
},
{
obj2:{key2:'value2'}
}
]
const getValuesFromObj = (obj) => {
if(typeof obj === 'string')
return obj;
return Object.keys(obj).map(key => {
return getValuesFromObj(obj[key]);
}).reduce((acc,v) => {
return acc.concat(v);
},[]);
}
const r2 = getValuesFromObj(arr1);
console.log(r2);

How to add non duplicate objects in an array in javascript?

I want to add non-duplicate objects into a new array.
var array = [
{
id: 1,
label: 'one'
},
{
id: 1,
label: 'one'
},
{
id: 2,
label: 'two'
}
];
var uniqueProducts = array.filter(function(elem, i, array) {
return array.indexOf(elem) === i;
});
console.log('uniqueProducts', uniqueProducts);
// output: [object, object, object]
live code
I like the class based approach using es6. The example uses lodash's _.isEqual method to determine equality of objects.
var array = [{
id: 1,
label: 'one'
}, {
id: 1,
label: 'one'
}, {
id: 2,
label: 'two'
}];
class UniqueArray extends Array {
constructor(array) {
super();
array.forEach(a => {
if (! this.find(v => _.isEqual(v, a))) this.push(a);
});
}
}
var unique = new UniqueArray(array);
console.log(unique);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.16.4/lodash.min.js"></script>
Usually, you use an object to keep track of your unique keys. Then, you convert the object to an array of all property values.
It's best to include a unique id-like property that you can use as an identifier. If you don't have one, you need to generate it yourself using JSON.stringify or a custom method. Stringifying your object will have a downside: the order of the keys does not have to be consistent.
You could create an objectsAreEqual method with support for deep comparison, but this will slow your function down immensely.
In two steps:
var array=[{id:1,label:"one"},{id:1,label:"one"},{id:2,label:"two"}];
// Create a string representation of your object
function getHash(obj) {
return Object.keys(obj)
.sort() // Keys don't have to be sorted, do it manually here
.map(function(k) {
return k + "_" + obj[k]; // Prefix key name so {a: 1} != {b: 1}
})
.join("_"); // separate key-value-pairs by a _
}
function getHashBetterSolution(obj) {
return obj.id; // Include unique ID in object and use that
};
// When using `getHashBetterSolution`:
// { '1': { id: '1', label: 'one' }, '2': /*etc.*/ }
var uniquesObj = array.reduce(function(res, cur) {
res[getHash(cur)] = cur;
return res;
}, {});
// Convert back to array by looping over all keys
var uniquesArr = Object.keys(uniquesObj).map(function(k) {
return uniquesObj[k];
});
console.log(uniquesArr);
// To show the hashes
console.log(uniquesObj);
You can use Object.keys() and map() to create key for each object and filter to remove duplicates.
var array = [{
id: 1,
label: 'one'
}, {
id: 1,
label: 'one'
}, {
id: 2,
label: 'two'
}];
var result = array.filter(function(e) {
var key = Object.keys(e).map(k => e[k]).join('|');
if (!this[key]) {
this[key] = true;
return true;
}
}, {});
console.log(result)
You could use a hash table and store the found id.
var array = [{ id: 1, label: 'one' }, { id: 1, label: 'one' }, { id: 2, label: 'two' }],
uniqueProducts = array.filter(function(elem) {
return !this[elem.id] && (this[elem.id] = true);
}, Object.create(null));
console.log('uniqueProducts', uniqueProducts);
Check with all properties
var array = [{ id: 1, label: 'one' }, { id: 1, label: 'one' }, { id: 2, label: 'two' }],
keys = Object.keys(array[0]), // get the keys first in a fixed order
uniqueProducts = array.filter(function(a) {
var key = keys.map(function (k) { return a[k]; }).join('|');
return !this[key] && (this[key] = true);
}, Object.create(null));
console.log('uniqueProducts', uniqueProducts);
You can use reduce to extract out the unique array and the unique ids like this:
var array=[{id:1,label:"one"},{id:1,label:"one"},{id:2,label:"two"}];
var result = array.reduce(function(prev, curr) {
if(prev.ids.indexOf(curr.id) === -1) {
prev.array.push(curr);
prev.ids.push(curr.id);
}
return prev;
}, {array: [], ids: []});
console.log(result);
.as-console-wrapper{top:0;max-height:100%!important;}
If you don't know the keys, you can do this - create a unique key that would help you identify duplicates - so I did this:
concat the list of keys and values of the objects
Now sort them for the unique key like 1|id|label|one
This handles situations when the object properties are not in order:
var array=[{id:1,label:"one"},{id:1,label:"one"},{id:2,label:"two"}];
var result = array.reduce(function(prev, curr) {
var tracker = Object.keys(curr).concat(Object.keys(curr).map(key => curr[key])).sort().join('|');
if(!prev.tracker[tracker]) {
prev.array.push(curr);
prev.tracker[tracker] = true;
}
return prev;
}, {array: [], tracker: {}});
console.log(result);
.as-console-wrapper{top:0;max-height:100%!important;}

Changing the case of JavaScript object keys

I have following object.
var obj = [{
Address1: "dd",
Address2: "qww",
BankAccNo: "44",
BankBranchCode: "44",
BloodGrp: "A+"
},
{
Address1: "dd",
Address2: "qww",
BankAccNo: "44",
BankBranchCode: "44",
BloodGrp: "A+"
}];
How can I make all of the keys uppercase?
I want to be able to access values like this : - obj[0].ADDRESS1
obj = obj.map( function( item ){
for(var key in item){
var upper = key.toUpperCase();
// check if it already wasn't uppercase
if( upper !== key ){
item[ upper ] = item[key];
delete item[key];
}
}
return item;
});
http://jsfiddle.net/07xortqy/
Loop over all the properties in the object (with for in)
Use .toUpperCase() to get the uppercase version of the property name
Copy the value from the original property to the uppercase version
delete the original property
For anyone looking for a solution working with objects, arrays, and nested objects or arrays:
// rename function depending on your needs
const capitalizeKeys = (obj) => {
const isObject = o => Object.prototype.toString.apply(o) === '[object Object]'
const isArray = o => Object.prototype.toString.apply(o) === '[object Array]'
let transformedObj = isArray(obj) ? [] : {}
for (let key in obj) {
// replace the following with any transform function
const transformedKey = key.replace(/^\w/, (c, _) => c.toUpperCase())
if (isObject(obj[key]) || isArray(obj[key])) {
transformedObj[transformedKey] = capitalizeKeys(obj[key])
} else {
transformedObj[transformedKey] = obj[key]
}
}
return transformedObj
}
const t = {
test1: 'hello',
test2: {
aa: 0,
bb: '1',
cc: [ 3, '4', 'world']
},
test3: [{
aa: 5,
bb: '6'
}, {
cc: [ 'hello', 'world', 7 ]
}
]
}
console.log(JSON.stringify(capitalizeKeys(t)))
(this function is to be adapted since I only had to capitalize the first letter, and there is no need for the helper functions to be nested)
$.each(obj, function(i, parent) {
$.each(parent, function(key, record) {
parent[ key.toUpperCase() ] = record[key]; //rename key
delete parent[key]; //delete old key
});
});
let obj = [
{ Address1: "dd",Address2: 'qww',BankAccNo: 44,BankBranchCode: 44,BloodGrp: 'A+' },
{ Address1: "dd",Address2: 'qww',BankAccNo: 44,BankBranchCode: 44,BloodGrp: 'A+' }
];
const uppercaseKeys = (elem) => {
let newObject = {}
Object.keys(elem).reduce( (acc, key, allKeys) => {
acc[key.toUpperCase()] = elem[key]
delete elem[key]
return acc
}, elem)
return newObject
}
obj.forEach( o => uppercaseKeys )
console.log(obj)
You can now also use Object.fromEntries() in combination with Object.entries() - have a look at the Object transformations section.
const obj2 = obj1.map(item => Object.fromEntries(Object.entries(item).map(([key, val]) => [
key.toUpperCase(),
val
])));
I've detailed the steps below:
// Iterate through each item in array
const obj2 = obj1.map(item => {
// Object.entries() method returns array of object's own enumerable string-keyed property [key, value] pairs,
// in the same order as that provided by a for...in loop
const entries = Object.entries(item);
// Convert keys to uppercase
const uppercaseEntries = entries.map(([key, val]) => [
key.toUpperCase(),
val
]);
// Object.fromEntries() method transforms a list of key-value pairs into an object.
return Object.fromEntries(uppercaseEntries);
});`
https://jsfiddle.net/buj5y32x/3/
For wider support, you are better off using Object.keys() with Array.reduce().
const obj2 = obj1.map(item =>
Object.keys(item).reduce((accumulator, key) => {
// accumulator is the new object we are creating
accumulator[key.toUpperCase()] = item[key];
return accumulator;
}, {})
);
https://jsfiddle.net/qf81ezsy/
You could just loop through them and add new entries?
for (index in obj) {
for (key in obj[index]) {
obj[index][key.toUpperCase()] = obj[key];
}
}

Categories

Resources