Convert dot notation strings to array objects,
Eg.,
let obj = { 'user-0-address-pincode': 665766, 'user-0-address-city': 'Chennai', 'user-1-address-pincode': 32432, 'user-1-address-city': 'Bangalore'};
// Expectation output will be
{
user: [
{
address: {pincode: 665766, city: 'Chennai'}
},
{
address: {pincode: 32432, city: 'Bangalore'}
}
]
}
Please help me to resolve this problem.
You can use reduce and split methods to create a function that will take your object with keys as paths and then based on those keys create nested structure.
let obj = {
'user-0-address-pincode': 665766,
'user-0-address-city': 'Chennai',
'user-1-address-pincode': 32432,
'user-1-address-city': 'Bangalore'
};
function parse(data) {
return Object.keys(obj).reduce((r, k) => {
k.split('-').reduce((a, e, i, arr) => {
const next = arr[i + 1]
if (!next) return a[e] = data[k]
else return a[e] || (a[e] = (!isNaN(+next) ? [] : {}))
}, r)
return r;
}, {})
}
const result = parse(obj)
console.log(result)
Related
I have an array of objects which looks like this:
const childrens = [
{ orderName: "#1004" },
{ orderName: "#1006" },
{ orderName: "#1007" },
{ orderName: "#1005" },
{ deliveryDate: "25-25-25" },
{ signature: "xq" },
];
I want this to be converted into object and have values of same keys as an array of values like this
{
orderName: [ '#1004', '#1006', '#1007', '#1005' ],
deliveryDate: [ '25-25-25' ],
signature: [ 'xq' ]
}
The way i'm doing this right now is by using a reduce function like this
_.reduce(
childrens,
(acc, cur) => {
const pairs = _.chain(cur).toPairs().flatten().value();
if (acc[pairs[0]] === undefined) {
acc[pairs[0]] = [pairs[1]];
} else {
acc[pairs[0]].push(pairs[1]);
}
return acc;
},
{},
);
I'm wondering if there's a cleaner way using built-in lodash functions?
You can use _.mergeWith() with array spread. When merging 2 values, check if the 1st (a) is still undefined (the empty object), if so return the 2nd item wrapped in an array, if it exists, use array spread to concat them:
const children = [{"orderName":"#1004"},{"orderName":"#1006"},{"orderName":"#1007"},{"orderName":"#1005"},{"deliveryDate":"25-25-25"},{"signature":"xq"}];
const result = _.mergeWith({}, ...children, (a, b) =>
_.isUndefined(a) ? [b] : [...a, b]
);
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js" integrity="sha512-WFN04846sdKMIP5LKNphMaWzU7YpMyCU245etK3g/2ARYbPK9Ub18eG+ljU96qKRCWh+quCY7yefSmlkQw1ANQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
If you don't use lodash, you can do like below.
const children = [
{ orderName: "#1004" },
{ orderName: "#1006" },
{ orderName: "#1007" },
{ orderName: "#1005" },
{ deliveryDate: "25-25-25" },
{ signature: "xq" },
];
const output = children.reduce((a, b) => {
const key = Object.keys(b)[0];
if (!a[key]) {
a[key] = [b[key]];
} else {
a[key].push(b[key]);
}
return a;
}, {});
console.log(output);
Something like this will work on modern JS platforms (added bonus, you don't need lodash to use reduce). Essentially:
loop over each item in childrens
get the key(s) for that object
loop over the key(s)
if result doesn't have that key set it to an empty array
push the value for the key to the relevant array on the result
const desiredOutput = childrens.reduce(
(acc, current) => {
const keys = Object.keys(current);
keys.forEach(key => {
if (!acc[key]) {
acc[key] = [];
}
acc[key].push(current[key]);
});
return acc;
},
{}
);
You can simply get the result using vanilla JS using reduce and Object.entries
(acc[prop] = acc[prop] ?? []).push(value);
const childrens = [
{ orderName: "#1004" },
{ orderName: "#1006" },
{ orderName: "#1007" },
{ orderName: "#1005" },
{ deliveryDate: "25-25-25" },
{ signature: "xq" },
];
const result = childrens.reduce((acc, curr) => {
const [[prop, value]] = Object.entries(curr);
(acc[prop] = acc[prop] ?? []).push(value);
return acc;
}, {});
console.log(result);
I would like to create a tree-like nested structure as obj (later JSON) but have struggling to do that properly.
I want to convert this:
root/app/index.html
into this:
{
type: 'box',
name: 'root'
children: {
type: 'box',
name: 'app',
children: {
type: 'item',
name: 'index.html'
}
}
}
How I can do that by code any advice?
You could also use a recursive approach:
let str = 'root/app/index.html';
console.log(convertToNestedTree(str))
function convertToNestedTree(str) {
let members = str.split('/');
if(members.length === 1) {
return ({type:'item', name:str});
}
return ({type:'box', name:members[0],
children:convertToNestedTree(members.slice(1).join('/'))
});
}
You could create a function that takes string and split it into array and then you could use reduce method on that array to build nested structure.
const string = 'root/app/index.html';
const parse = str => {
const result = {}
str.split('/').reduce((r, name, i, a) => {
Object.assign(r, {name, type: a[i + 1] ? 'box' : 'item'})
return a[i + 1] ? (r.children = {}) : r
}, result)
return result;
}
console.log(parse(string))
I have data array object like this:
const data = [
{Name: "dian", Job: "programmer"},
{Name: "dian", Job: "gamer"},
{Name: "candra", Job: "programmer"},
]
My goal is to create new data where a have same value join b.
Example output:
const new_data = [
{Name: "dian", Jobs: [{Job: "programmer"}, {Job: "gamer"}]},
{Name: "candra", Jobs: [{Job: "programmer"}]},
]
I think you can use Array.prototype.reduce() to achive your goal. From the documentation:
The reduce() method executes a reducer function (that you provide) on each element of the array, resulting in a single output value.
One possible solution:
const data = [
{Name:'dian', Job:'programer'},
{Name:'dian', Job:'gamers'},
{Name:'candra', Job:'programer'}
];
const result = data.reduce((a, current) => {
const found = a.find(f => f.Name === current.Name);
if (found) {
found.Jobs.push({Job: current.Job});
} else {
a.push({
Name: current.Name,
Jobs: [
{ Job: current.Job }
]
});
}
return a;
}, []);
console.log(result);
I hope that helps!
use reduce.
const data = [
{ Name: "dian", Job: "programer" },
{ Name: "dian", Job: "gamers" },
{ Name: "candra", Job: "programer" }
];
const output = Object.values(data.reduce((a, { Name, Job }, i) => {
if (!a[Name]) {
a[Name] = { Name, Jobs: [] };
}
a[Name].Jobs.push({ Job });
return a;
}, {}));
console.log(output);
Here's one approach:
const combineJobs = (data) =>
Object .values (data .reduce (
(a, {Name, Job}, _, __, curr = a [Name] || {Name, jobs: []}) =>
({... a, [Name]: ({... curr, jobs: [... curr .jobs, {Job}]})}),
{}
))
const data = [{Name: "dian", Job: "programmer"}, {Name: "dian", Job: "gamer"}, {Name: "candra", Job: "programmer"}]
console .log (combineJobs (data))
We simply fold our objects into a structure that looks like
{
dian: {Name: 'dian', jobs: [Job:'programer'}, {Job: 'gamer'}]},
candra: {Name: 'candra', jobs: [Job:'programer'}]},
}
then use Object .values to turn it into an appropriate array.
One advantage of this technique is that if your data actually has additional fields not displayed in the question (imagine you have age, and avatar properties as well, for instance), you can extend it easily using a rest parameter:
const combineJobs = (data) =>
Object .values (data .reduce (
(a, {Name, Job, ...rest}, _, __, curr = a [Name] || {Name, jobs: [], ...rest}) =>
// ^^^^^^^ ^^^^^^^
({... a, [Name]: ({... curr, jobs: [... curr .jobs, {Job}]})}),
{}
))
and all those additional parameters would be included.
function modifyArray(data) {
function getNewObject(data) {
const newObject = {
Name: data.Name
};
Object.keys(data).filter(key => key !== 'Name').forEach(key => {
newObject[key+'s'] = [];
newObject[key+'s'].push({
[key]: data[key]
});
});
return newObject;
}
function appendData(obj, data) {
Object.keys(data).filter(key => key !== 'Name').forEach(key => {
obj[key+'s'].push({
[key]: data[key]
});
});
}
const reqArray = [];
data.forEach(d => {
const objToModify = reqArray.find(a => a.Name === d.Name);
if (!objToModify) {
reqArray.push(getNewObject(d));
} else {
appendData(objToModify, d);
}
});
return reqArray;
}
let data =[
{Name:'dian', Job:'programer' },
{Name:'dian', Job:'gamers' },
{Name:'candra', Job:'programer' }
];
console.log(modifyArray(data));
I'm working on an existing project that takes query parameters in an oddly formatted string dot notation. But they must be converted into objects before processing. This is currently being performed with conditionals on specific keys by name.
How can this be performed dynamically? Below you will find an example of the input and desired output.
Input:
{
date.gte: '2019-01-01',
date.lt: '2020-01-01'
}
Output:
{
date: {
gte: '2019-01-01',
lt: '2020-01-01'
}
}
You could use reduce and split methods to split each key into array and build nested structure based on that array.
const data = {
'date.gte': '2019-01-01',
'date.lt': '2020-01-01'
}
const result = Object.entries(data).reduce((r, [k, v]) => {
k.split('.').reduce((a, e, i, ar) => {
return a[e] || (a[e] = ar[i + 1] ? {} : v)
}, r)
return r;
}, {})
console.log(result)
By you saying "oddly formatted string dot notation" I assume you mean "date.gte" & "date.lt"
const input = {
"date.gte": "2019-01-01",
"date.lt": "2020-01-01"
};
const res = Object.keys(input).reduce(
(result, current) => {
const [, operator] = current.split(".");
result.date[operator] = input[current];
return result;
},
{ date: {} }
);
console.log(res);
Here's an improvement on Dan's answer that doesn't rely on knowing the key-value pairs in the original object. As much as Nenad's answer blows this out of the water, I worked for too long on this to not post it :)
const formatter = (weirdObject, s = '.') => Object.keys(weirdObject).reduce((acc, cur) => {
const [parent, child] = cur.split(s);
if (!acc[parent]) acc[parent] = {};
acc[parent][child] = weirdObject[cur];
return acc;
}, {});
// -- Demonstration:
const input1 = {
"date.gte": "2019-01-01",
"date.lt": "2020-01-01"
};
const input2 = {
"person:name": "Matt",
"person:age": 19
};
const res1 = formatter(input1);
const res2 = formatter(input2, ':');
console.log(res1);
console.log(res2);
I have a requirement to replace the available keys with the desired keys in an object for which I was trying to execute below code, which later I found out to be incorrect usage of filter for desired output. hence I need help in getting the desired results using es6 array functions.
const columns = Object.keys(someArray).filter((columnName) => {
if (someCheck === "somecheck") {
if (columnName === 'MyName') {
const newcolumnName = `Pranav`;
return newcolumnName;
} else if (columnName === 'YourName') {
const newcolumnName = `Alex`;
return newcolumnName;
}
} else {
return (columnName !== 'sometingelse') ? columnName : '';
}
}
);
Here the someArray is as below:
someArray{
abc:"djfhdjf",
xyz:"ssss",
MyName:"onename",
YourName:"somename",
sometingelse:'somevalue'
}
I am expecting columns to be:
columns{
abc:"djfhdjf",
xyz:"ssss",
Pranav:"onename",
Alex:"somename",
sometingelse:'somevalue'
}
Please suggest how can I achieve the above expected output?
Note: I dont want to use function keyword in callbacks to avoid eslint errors
You could filter the wanted keys for replacement and replace the keys by using a new key and eleting the old one.
const
object = { abc: "djfhdjf", xyz: "ssss", MyName: "onename", YourName: "somename", sometingelse: 'somevalue' },
replacements = { MyName: 'Pranav', YourName: 'Alex', sometingelse: '' };
Object
.keys(object)
.filter(k => k in replacements)
.forEach(k => {
object[replacements[k]] = object[k];
delete object[k];
});
console.log(object);
For generating an object, you could map new objects and assign them to a single object.
const
object = { abc: "djfhdjf", xyz: "ssss", MyName: "onename", YourName: "somename", sometingelse: 'somevalue' },
replacements = { MyName: 'Pranav', YourName: 'Alex', sometingelse: '' },
result = Object.assign(...Object
.entries(object)
.map(([k, v]) => ({ [k in replacements ? replacements[k] : k]: v }))
);
console.log(result);
const obj = {
abc: 'djfhdjf',
xyz: 'ssss',
MyName: 'onename',
YourName: 'somename',
sometingelse: 'somevalue'
};
const newObj = Object.keys(obj).reduce((acc, key) => {
if (key === 'MyName') {
acc.newMyName = obj[key];
} else if (key === 'YourName') {
acc.newYourName = obj[key];
} else {
acc[key] = obj[key];
}
return acc;
}, {});
console.log('newObj = ', newObj);
Here is my approach, a bit long solution, but its on purpose so you can see how to do it simple without too much abstraction:
const someArray = {
abc:"djfhdjf",
xyz:"ssss",
MyName:"onename",
YourName:"somename",
sometingelse:'somevalue'
}
let foo = Object.keys(someArray).map(key => {
if(key === 'MyName') {
return 'Alex'
} else if(key === 'YourName') {
key = 'Pranav'
}
return key;
})
let bar = Object.entries(someArray).map((el, i) => {
el[0] = res[i];
return el;
})
let baz = r.reduce((acc, el)=>{
acc[`${el[0]}`] = el[1];
return acc;
},{})
console.log(baz);
You could use .reduce like so. It uses a similar idea that Nina proposed by using an object to hold your replacements. Here I have used the spread syntax to add the changed key to the accumulated object, along with it's associated value.
const someArray = {abc: "djfhdjf", xyz: "ssss", MyName: "onename", YourName: "somename", sometingelse: 'somevalue'},
toUse = {MyName: "Pranav", YourName: "Alex"}, // define the keys you want to change and what they should change to
res = Object.keys(someArray).reduce((acc, key) =>
({...acc, [key in toUse ? toUse[key] : key]:someArray[key]})
, {});
console.log(res);
I am running a reduce on the keys of some array starting with an empty object. The ...acc spreads out all the properties in the reduced object. ...{ [keysMap[key] || key]: obj[key] } checks if the current key is present in keysMap.If it is present,it uses that key (keysMap[key]) otherwise it just uses the keys of the existing object.(|| key).Hope that makes sense
const renameKeys = (keysMap, obj) =>
Object.keys(obj).reduce(
(acc, key) => ({
...acc,
...{ [keysMap[key] || key]: obj[key] }
}),
{}
)
const columns = renameKeys({'MyName':'Pranav','YourName':'Alex'},someArray)