How to remove duplicates with extra keys from an array - javascript

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.

Related

Unstring an object property name javascript

I have an object similar to this:
const obj = {
id: 1,
name: {
"english-us": "John",
"english-uk": "John",
"italian-eu": "Giovanni",
},
};
I want to transfrorm every property name that is a string into a non-string one, like this:
const obj = {
id: 1,
name: {
english_us: "John",
english_uk: "John",
italian_eu: "Giovanni",
},
};
I can't modify the original object. I get it from an axios request.
You could use regex with stringify
let output = JSON.parse(JSON.stringify(obj).replace(/"(.*?)":.*?,?/g,
key=>key.replace(/\-/g, `_`)));
Output
console.log(JSON.stringify(output, null, 4));
/*
{
"id": 1,
"name": {
"english_us": "John",
"english_uk": "John",
"italian_eu": "Giovanni"
}
}*/
If you can copy the object, you could check this solution for declaring the attributes:
link
There are a few ways of achieving this. This example has a function that converts the key on every iteration of the name entries. A new names object is updated with these properties, and is later folded into a new object along with the existing properties of the original object.
const obj = {
id: 1,
name: {
"english-us": "John",
"english-uk": "John",
"italian-eu": "Giovanni",
},
};
const convert = (key) => key.replace('-', '_');
const updatedName = {};
for (const [key, value] of Object.entries(obj.name)) {
updatedName[convert(key)] = value;
}
const newObj = { ...obj, name: updatedName };
console.log(newObj);
You can convert object to JSON and convert back.
const obj = {
id: 1,
name: {
"english-us": "John",
"english-uk": "John",
"italian-eu": "Giovanni",
},
};
console.log(JSON.parse(JSON.stringify(obj)))
Two ways to clone the object and rename all keys from its name property
const obj = {
id: 1,
name: {
"english-us": "John",
"english-uk": "John",
"italian-eu": "Giovanni",
},
};
// clone obj
const myObj = window.structuredClone ?
structuredClone(obj) : JSON.parse(JSON.stringify(obj));
// rename all keys in myObj.name
Object.keys(myObj.name).forEach(key => {
myObj.name[key.replace(/\-/g, `_`)] = myObj.name[key];
delete myObj.name[key];
});
console.log(myObj.name.english_us);
// obj is untouched
console.log(obj.name[`english-us`]);
// myObj.name[`english-us`] does not exist
console.log(myObj.name[`english-us`]);
// alternative: clone and rename in one go
const myObjClone = {
...obj,
name: Object.fromEntries(
Object.entries(obj.name)
.reduce( (acc, [k, v]) =>
[ ...acc, [ k.replace(/\-/g, `_`), v ] ] , [] ) )
};
console.log(myObjClone.name.italian_eu);
// obj is untouched
console.log(obj.name[`italian-eu`]);
// myObjClone.name[`italian-eu`] does not exist
console.log(myObjClone.name[`italian-eu`]);

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 merge two objects containing same keys but different values

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' }
]

Inside an Object.value loop how can I get the key

I have to create match condition based on an array my array will look like below
var groupData={
A:[
{rollnum: 1, name:'Arya', age:15},
{rollnum: 2, name:'Aryan', age:15}
],
B:[
{rollnum:11, name:'Biba', age:15},
{rollnum:12, name:'Bimisha', age:15}
]
}
I am looping using for loop. How can reduce the loops. Can any one suggest me a proper way for this
Object.values(groupData).flat().forEach((rowitem)=>{
query={};
Object.keys(rowitem).forEach(eachField=>{
query[eachField]["$in"].push(rowitem[eachField])
});
fullarray[Object.keys(groupData)]=matchQuery;
})
I need an output (fullarray) like below
{
'A':{
rollnum:{'$in':[1,2]},
name: {'$in':['Arya', 'Aryan']},
age: {'$in':[15]}
},
'B':{
rollnum:{'$in':[11,12]},
name: {'$in':['Biba', 'Bimisha']},
age: {'$in':[15]}
}
}
Here 'A' 'B' is not coming correctly
Don't use Object.values() since that discards the A and B keys.
Use nested loops, one loop for the properties in the object, and a nested loop for the arrays.
You need to create the nested objects and arrays before you can add to them.
var groupData = { A:
[ { rollnum: 1,
name: 'Arya',
age:15},
{ rollnum: 2,
name: 'Aryan',
age:15}, ],
B:
[ { rollnum: 11,
name: 'Biba',
age:15},
{ rollnum: 12,
name: 'Bimisha',
age:15} ] }
result = {};
Object.entries(groupData).forEach(([key, arr]) => {
if (!result[key]) {
result[key] = {};
}
cur = result[key];
arr.forEach(obj => {
Object.entries(obj).forEach(([key2, val]) => {
if (!cur[key2]) {
cur[key2] = {
"$in": []
};
}
cur[key2]["$in"].push(val);
});
});
});
console.log(result);

Object.entries alternative transforming array of object into strings

I tried to print "person[0].name: a" and "person[1].name: b" and so on, based on this person array of object:
I used object entries twice, any other way I can make the loop more efficient?
const person = [{
name: 'a',
}, {
name: 'b'
}]
Object.entries(person).forEach(([key, value]) => {
Object.entries(value).forEach(([key2, value2]) => {
console.log(`person[${key}].${key2}`, ':', value2)
})
})
You could take a recursive approach and hand over the parent path.
const
show = (object, parent) => {
const wrap = Array.isArray(object) ? v => `[${v}]` : v => `.${v}`;
Object.entries(object).forEach(([k, v]) => {
if (v && typeof v === 'object') show(v, parent + wrap(k));
else console.log(parent + wrap(k), v);
});
},
person = [{ name: 'a' }, { name: 'b' }];
show(person, 'person');
You don't really need the first call. person is already an Array.
const person = [{
name: 'a',
}, {
name: 'b'
}]
person.forEach((value, key) => {
Object.entries(value).forEach(([key2, value2]) => {
console.log(`person[${key}].${key2}`, ':', value2)
})
})
Just in case forEach is for side effects. If you want to create another array with transformed values you better use map/flatMap instead.
const person = [{
name: 'a',
}, {
name: 'b'
}]
const transformed = person.flatMap((value, key) => {
return Object.entries(value).map(([key2, value2]) => `person[${key}].${key2}:${value2}`)
})
console.log(transformed)
You can also try something like this, with only one forEach() loop
const person = [{
name: 'a',
},{
name: 'b'
}];
person.forEach((el,i) => {
let prop = Object.keys(el).toString();
console.log(`person[${i}].${prop}`, ':', el[prop])
});

Categories

Resources