Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 25 days ago.
Improve this question
I have multiple objects:
const obj1 = {
db: {
url: "mongodb://localhost:27017",
},
};
const obj2 = {
db: {
user: "admin",
},
};
const obj3 = {
token: {
auth: {
secret: "*****",
},
},
};
How can merge into a single object like this:
{
db: {
url: "mongodb://localhost:27017",
user: "admin"
},
token: {
auth: {
secret: "*****"
}
}
}
I just tried something like this: Object.assign(obj1, obj2) but is not what I want.
I suggest correcting the objects that aren't valid, as pointed out in the comments. Other than this, the following function works for as many objects as you want.
const obj1 = {
db: {
url: "mongodb://localhost:27017",
},
};
const obj2 = {
db: {
user: "admin",
},
};
const obj3 = {
token: {
auth: {
secret: "*****",
},
},
};
function merge() {
const result = {};
for (let i = 0; i < arguments.length; i++) {
const obj = arguments[i];
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
if (typeof obj[key] === "object") {
result[key] = merge(result[key], obj[key]);
} else {
result[key] = obj[key];
}
}
}
}
return result;
}
const merged = merge(obj1, obj2, obj3);
console.log(merged);
Related
I'm trying to transform the object received from the to a format used in backend.
I receive this object
{
'User.permissions.user.view.dashboard': true,
'Admin.permissions.user.view.dashboard': true,
'Admin.permissions.admin.view.dashboard': true,
}
The first part of the key (User, Admin) is the role name, the rest is the role. I need to combine this object into an object that has role names as keys for arrays containing the permission strings. The final result should look like this
{
'User': [
'permissions.user.view.dashboard'
],
'Admin': [
'permissions.user.view.dashboard',
'permissions.user.admin.dashboard;
]
}
So far I managed to get this result, but I'm not sure how to combine the results
const data = JSON.parse(req.body)
const permissions = Object.keys(data).map((key) => {
const permParts = key.split('.')
return {[permParts[0]]: permParts.slice(1).join('.')}
})
console.log(permissions);
[
{ User: 'permissions.user.view.dashboard' },
{ Admin: 'permissions.admin.view.dashboard' },
{ Admin: 'permissions.user.view.dashboard' }
]
const roleData = {
'User.permissions.user.view.dashboard': true,
'Admin.permissions.user.view.dashboard': true,
'Admin.permissions.admin.view.dashboard': true,
};
const mappedData = Object.keys(roleData).reduce((acc, entry) => {
const dotIndex = entry.indexOf('.');
const parts = [entry.slice(0,dotIndex), entry.slice(dotIndex+1)];
acc[parts[0]] ??= [];
acc[parts[0]].push(parts[1]);
return acc;
}, {});
console.log(mappedData);
You can use reduce function:
const result = permissions.reduce((acc, cur) => {
const key = Object.keys(cur)[0]
if (acc[key]) {
acc[key] = [...acc[key], cur[key]]
} else {
acc[key] = [cur[key]]
}
return acc
}, {})
This question already has answers here:
How to set object property (of object property of..) given its string name in JavaScript?
(16 answers)
Closed 1 year ago.
I have this object {country:{town:{company:{boss:{}}}}} and this string country.town.cityhouse.major, I need to updatr the object from the string, but keeping the previous properties and data.
{
country: {
town: {
company: {
boss: {}
},
cityhouse: {
major: {}
}
}
}
}
This is what I have so far:
function updateObj(object, path) {
const newPath = path.split('.');
let temp = {...object};
for (let i = 0; i < newPath.length; i++) {
let mid = temp[newPath[i]];
if (mid) {
temp = mid;
} else {
temp[newPath[i]] = {};
}
}
return temp;
}
const obj = { country: { town: { company: { boss: {} }}}};
const r = updateObj(obj, 'country.town.cityhouse.major');
console.log(r);
but it responds:
{
company: {
boss: {}
},
cityhouse: {},
major: {}
}
Any hint on this?
You can clean this up a little using logical nullish assignment(??=) and a for...of loop.
function updateObj(object, path) {
let result = { ...object };
let temp = result;
for (const k of path.split('.')) {
temp = temp[k] ??= {};
}
return result;
}
const obj = { country: { town: { company: { boss: {} } } } };
const r = updateObj(obj, 'country.town.cityhouse.major');
console.log(r);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Recursive option
function updateObj(object, path) {
const result = { ...object };
const [prop, ...pathArr] = path.split('.');
result[prop] = pathArr.length
? updateObj((result[prop] ??= {}), pathArr.join('.'))
: (result[prop] ??= {});
return result;
}
const obj = { country: { town: { company: { boss: {} } } } };
const r = updateObj(obj, 'country.town.cityhouse.major');
console.log(r);
.as-console-wrapper { max-height: 100% !important; top: 0; }
This code would normally be written recursively, so you need to have a variable to hold the current scope (the path you walked on through the object) to do the job, and so i created the variable called scope to do that job.
function updateObj(object, path) {
const newPath = path.split('.');
let temp = {...object};
let scope = temp;
for (let i = 0; i < newPath.length; i++) {
let cur = scope[newPath[i]];
if (!cur) {
scope = scope[newPath[i]] = {};
} else {
scope = cur;
}
}
return temp;
}
const obj = { country: { town: { company: { boss: {} }}}};
const r = updateObj(obj, 'country.town.cityhouse.major');
console.log(r);
This is a recursive implementation, but since it requires copying the object and passing it to the next recursive call it's not an efficient one, If i find a better more efficient implementation, I'll update this.
function updateObjRec(object, path, depth = 0) {
// base case
// if depth is equal to path length then it's over
if (depth === path.length) {
return {};
}
const cur = path[depth];
const scope = object[cur];
// here we have 2 cases
// current field already exists
update = {};
if (scope) {
update = updateObjRec({ ...scope}, path, depth + 1);
} else {
update = updateObjRec({}, path, depth + 1);
}
// merge with given object
object[cur] = update;
return object
}
const obj = { country: { town: { company: { boss: {} }}}};
const r = updateObjRec(obj, 'country.town.cityhouse.major'.split('.'));
console.log(r);
Update
The recursive code can be rewritten this way to be more efficient
function updateObj(obj, path) {
const temp = { ...obj };
const p = path.split('.');
const updateRec = (scope, depth = 0) => {
// base case
if (depth === p.length) return;
const cur = p[depth];
if (!scope[cur]) {
scope[cur] = {};
}
updateRec(scope[cur], depth + 1);
}
updateRec(temp);
return temp;
}
const obj = { country: { town: { company: { boss: {} }}}};
const r = updateObj(obj, 'country.town.cityhouse.major');
console.log(r);
This isn't ideal, but it's a start.
Basically, you're returning temp, which is the lowest layer. you want to return the root.
Also when you can't find the next layer, you were creating it, but you weren't updating temp.
function updateObj(object, path) {
const newPath = path.split('.');
let temp = {...object};
const result = temp; // need to return this
for (let i = 0; i < newPath.length; i++) {
let mid = temp[newPath[i]];
if (mid) {
temp = mid;
} else {
temp[newPath[i]] = {};
temp = temp[newPath[i]] // need this
}
}
return result;
}
const obj = { country: { town: { company: { boss: {} }}}};
const r = updateObj(obj, 'country.town.cityhouse.major');
console.log(r);
I think this is what you want to achieve
const obj = {
country: {
town: {
company: { boss: {} },
},
},
};
obj.country.town.cityhouse = {
major: {},
};
console.log(obj);
This question already has answers here:
Clean way to keep original variable and destructure at the same time
(5 answers)
Closed 2 years ago.
I have the following function.
returnStateElement = (...elements) => {
const copy = Object.assign({}, this.state);
return elements.reduce((obj, key) => ({ ...obj, [key]: copy[key] }), {});
};
Working:
f = () => {
const dataSender = this.returnStateElement('email', 'password');
let { email, password } = dataSender;
console.log(dataSender,email,password);
}
Not working:
f2 = () => {
const { email, password } = dataSender = this.returnStateElement('email', 'password');
console.log(dataSender,email,password);
}
Is there a way to make a more compact type of assignment like f2()?
The following should work, you don't have to assign the results to an intermediate variable, you just need to provide the exact names that are in the returned object
f2 = () => {
const { email, password } = this.returnStateElement('email', 'password');
console.log(dataSender,email,password);
}
Example
const foo = () => {
return {
'a': '1',
'b': '2'
}
}
const bar = () => {
const { a, b } = foo();
console.log(a)
console.log(b)
}
bar();
// Logs 1, 2
f2 = () => {
const { email, password, ...rest } = this.returnStateElement('email', 'password');
console.log(email,password, rest);
// you can combine all like
console.log({...rest, email, password });
}
I am trying to learn how to cope with the objects and arrays and I saw many ways of iterating objects but recursing doesn't work for me and I don't understand why. What am I doing wrong?
I need to loop through an object and just slightly change something in an array. In my case, it's uppercasing the keys
Here is what I've got for now
const development = {
port: 8080,
db: {
username: "jkdfs",
password: "dsfsdg",
name: "kslfjskd",
test: { test: 12, another: 'value' }
},
token_secret: "jfkjdsj",
hash_rounds: "kjsfkljdfkl"
};
function throughObject(obj) {
let collection = {};
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
let value = obj[key];
if (typeof obj[key] !== 'object') {
collection[key.toUpperCase()] = value;
} else {
collection[key.toUpperCase()] = nestedObject(obj[key]);
}
}
function nestedObject(nested) {
const sub = {};
for (const k in nested) {
let v = nested[k];
if (typeof nested[k] !== 'object') {
sub[k.toUpperCase()] = v;
} else {
nestedObject(v);
}
}
return sub;
}
}
return collection;
}
const r = throughObject(development);
console.log(r);
When you're recursively calling the function on an object value, you still need to assign it to the sub object: sub[k.toUpperCase()] = nestedObject(v). Also, you don't need 2 different functions.
const development = {
port: 8080,
db: {
username: "jkdfs",
password: "dsfsdg",
name: "kslfjskd",
test: { test: 12, another: 'value' }
},
token_secret: "jfkjdsj",
hash_rounds: "kjsfkljdfkl"
};
function nestedObject(nested) {
const sub = {};
for (const k in nested) {
const v = nested[k];
if (typeof nested[k] !== 'object')
sub[k.toUpperCase()] = v;
else
sub[k.toUpperCase()] = nestedObject(v); // <- HERE
}
return sub;
}
console.log(nestedObject(development))
Here's a shorter version using Object.fromEntries()
const development={port:8080,db:{username:"jkdfs",password:"dsfsdg",name:"kslfjskd",test:{test:12,another:"value"}},token_secret:"jfkjdsj",hash_rounds:"kjsfkljdfkl"};
const convert = o =>
Object.fromEntries(
Object.entries(o).map(([k, v]) =>
[k.toUpperCase(), Object(v) === v ? convert(v) : v]
)
)
console.log(convert(development))
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 3 years ago.
Improve this question
How this code can be improved or optimized. Though it gives the required result. I want to write it with a better approach.
I am fetching data from www.contentful.com and then filtring it to get the localization with their keys. Note we have to use .reduce() function it is a requirement
import * as fs from 'fs';
const client = createClient();
interface IEntry {
id: string;
text: string;
}
interface IEntries {
[key: string]: { [key: string]: string };
}
export async function getLocalization() {
const entries = await client.getEntries<IEntry>({
skip: 0,
limit: 100,
locale: '*',
content_type: 'translation',
});
let enEntries: IEntries = entries.items
.map((e: any) => e.fields)
.reduce(
(ret, entry) => ({
...ret,
[entry.id.fi]: entry.text.en,
}),
{},
);
let fiEntries: IEntries = entries.items
.map((e: any) => e.fields)
.reduce(
(ret, entry) => ({
...ret,
[entry.id.fi]: entry.text.fi,
}),
{},
);
let svEntries: IEntries = entries.items
.map((e: any) => e.fields)
.reduce(
(ret, entry) => ({
...ret,
[entry.id.fi]: entry.text.sv,
}),
{},
);
const translations = {
['en']: { ...enEntries },
['fi']: { ...fiEntries },
['sv']: { ...svEntries },
};
const dir = './data';
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir);
}
fs.writeFileSync('data/translations.json', JSON.stringify(translations));
return true;
}
getLocalization();
Output can be found on this link (actual values have been removed): https://imgur.com/k3rzxWx
Your code looks quite fine, you can however add a helper to save the repetition:
// Can be replaced with Object.fromEntries(values.map(getDescriptor)) soon
function mapOf<E, V>(values: Array<E>, getDescriptor: (entry: E) => [string, V]): { [key: string]: V } {
const result = {};
for(const entry of values) {
const [key, value] = getDescriptor(entry);
result[key] = value;
}
return result;
}
const translations = {
en: mapOf(entries.items, it => ([it.fields.id.fi, it.fields.text.en])),
fi: mapOf(entries.items, it => ([it.fields.id.fi, it.fields.text.fi])),
sv: mapOf(entries.items, it => ([it.fields.id.fi, it.fields.text.sv])),
};
Or as another approach you could fill your translations object automatically when iterating the entries:
const translations = { en: {}, fi: {}, sv: {}, };
for(const it of entries.items) {
const key = it.fields.id.fi, values = it.fields.text;
for(const language in values) {
if(!translations[language]) continue;
translations[language][key] = values[language];
}
}
i think the following approach will be best in this case.
import { IFields, ITranslation } from './localization.types';
import * as fs from 'fs';
const client = createClient();
const en: ITranslation = {};
const fi: ITranslation = {};
const sv: ITranslation = {};
export async function getLocalization() {
const entries = await client.getEntries<IFields>({
skip: 0,
limit: 100,
locale: '*',
content_type: 'translation',
});
const localizations = entries.items
.map(e => e.fields)
.reduce(
(ret, entry) => {
ret.en[entry.id.fi] = entry.text.en === undefined ? 'no_value_found' : entry.text.en;
ret.fi[entry.id.fi] = entry.text.fi === undefined ? 'no_value_found' : entry.text.fi;
ret.sv[entry.id.fi] = entry.text.sv === undefined ? 'no_value_found' : entry.text.sv;
return ret;
},
{ en, fi, sv },
);
const dir = './data';
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir);
}
fs.writeFileSync('data/translations.json', JSON.stringify(localizations));
return true;
}
getLocalization();
And following are the types
export interface IId {
fi: string;
}
export interface IText {
en: string;
fi: string;
sv: string;
}
export interface IFields {
id: IId;
text: IText;
}
export interface ITranslation {
[key: string]: string;
}