JSON Path Syntax to merge two arrays - javascript

I have a jSOn with two arrays. I would like to merge all items of both arrays into a single array. Is there a jSOn path syntax i could use to give the desired output?
I have this:
[
"SomeArray",
{
"Branch1": {
"Branch2": {
"Branch3": {
"Array1": [
"Item1InArray1",
"Item2InArray1"
],
"Array2": [
"Item1InArray2",
"Item2InArray2"
]
}
}
}
}
]
And want this output: Its essentially two paths: [1].Branch1.Branch2.Branch3.Array1 and [1].Branch1.Branch2.Branch3.Array2
[
"Item1InArray1",
"Item2InArray1",
"Item1InArray2",
"Item2InArray2"
]

This simple example shows how you can achieve this having a full string-based path.
const arr = [
{
"Branch1": {
"Branch2": {
"Branch3": {
"Array1": [
"Item1InArray1",
"Item2InArray1"
],
"Array2": [
"Item1InArray2",
"Item2InArray2"
]
}
}
}
}
];
const paths = ["0.Branch1.Branch2.Branch3.Array1", "0.Branch1.Branch2.Branch3.Array2"];
const result = paths.map(path => path.split(".").reduce((unique, value) => unique[value], arr)).flat()
console.log(result)

You could simply get all nested arrays.
const
flat = item => item && typeof item === 'object'
? Array.isArray(item)
? item
: Object.values(item).flatMap(flat)
: [],
data = ["SomeArray", { Branch1: { Branch2: { Branch3: { Array1: ["Item1InArray1", "Item2InArray1"], Array2: ["Item1InArray2", "Item2InArray2"] } } } }],
items = data.flatMap(flat);
console.log(items);

{
"Branch1": {
"Branch2": {
"Branch3": {
"Array1": [
"Item1InArray1",
"Item2InArray1"
],
"Array2": [
"Item1InArray2",
"Item2InArray2"
]
}
}
}
}
]
let newArray = array1.reduce((main, curr)=> main.concat(curr.Branch1.Branch2.Array1, curr.Branch1.Branch2.Array1),[])

Related

Typescript flatten complex nested array

I'm still pretty new to Typescript so I've been looking at all the provided solutions but I'm still stuck with a complex nested array. I have the following structure:
data = [{
"property1_1": "value1_1",
"property1_2": "value1_2",
"property1_3": [
[{
"subproperty1_1_1": "subvalue1_1_1",
"subproperty1_1_2": "subvalue1_1_2"
}],
[{
"subproperty1_2_1": "subvalue1_2_1",
"subproperty1_2_2": "subvalue1_2_2"
}]
]
},
{
"property2_1": "value2_1",
"property2_2": "value2_2",
"property2_3": [
[{
"subproperty2_2_1": "subvalue2_2_1",
"subproperty2_2_2": "subvalue2_2_2"
}],
[{
"subproperty2_2_1": "subvalue2_2_1",
"subproperty2_2_2": "subvalue2_2_2"
}]
]
}
]
and I would like to achieve a simple array with objects looking like:
data = [{
"property1_1": "value1_1",
"property1_2": "value1_2",
"subproperty1_1_1": "subvalue1_1_1",
"subproperty1_1_2": "subvalue1_1_2",
"subproperty1_2_1": "subvalue1_2_1",
"subproperty1_2_2": "subvalue1_2_2"
},
{
"property2_1": "value2_1",
"property2_2": "value2_2",
"subproperty2_1_1": "subvalue2_1_1",
"subproperty2_1_2": "subvalue2_1_2",
"subproperty2_2_1": "subvalue2_2_1",
"subproperty2_2_2": "subvalue2_2_2"
}
]
I've already achieved to collect the subproperties into one array like doing:
const allDataConv = [];
data.forEach(dateninput => {
dateninput.propertyname.forEach(item => {
item.forEach(lastitem => {
allDataConv.push({key: lastitem.description, title: lastitem.name});
});
});
});
return allDataConv;
But that's not what I want to achieve. I'm stuck with the question: How can I apply the double nested objects into the corresponding parent object?
You could iterate over your object keys and check if the value is an array. If it's an array use flat(1) to flatten it and then iterate over it's children. Use Object.assign to add the child object keys to your root object and delete the key which had the array as a value.
Array.flat is an ESNext feature, you may need a polyfill for it in older browsers or use another for-of loop.
const input = data = [
{
"property1_1": "value1_1",
"property1_2": "value1_2",
"property1_3": [
[
{
"subproperty1_1_1": "subvalue1_1_1",
"subproperty1_1_2": "subvalue1_1_2"
}
],
[
{
"subproperty1_2_1": "subvalue1_2_1",
"subproperty1_2_2": "subvalue1_2_2"
}
],
],
},
{
"property2_1": "value2_1",
"property2_2": "value2_2",
"property2_3": [
[
{
"subproperty2_1_1": "subvalue2_2_1",
"subproperty2_1_2": "subvalue2_2_2"
}
],
[
{
"subproperty2_2_1": "subvalue2_2_1",
"subproperty2_2_2": "subvalue2_2_2"
}
]
]
}
];
function transformData(input) {
return input.map(obj => {
for(const prop in obj) {
if(obj.hasOwnProperty(prop) && Array.isArray(obj[prop])) {
for(const subObj of obj[prop].flat(1)) {
Object.assign(obj, subObj);
delete obj[prop];
}
}
}
return obj;
})
}
console.log(transformData(input));

how to change value of all nodes of json array

I have a json array with different key values and need to add a ServerUrl to the beginning of all node values using a loop without writing multiple statements to do that by using javascript:
"Urls": [
{ "getCar": "/getAllCars" },
{ "getPerson": "/getAllPersons" },
{ "getBook": "/getAllBooks" }
],
"ServerUrl": "http://192.168.1.1:3000"
The expected result must be:
"Urls": [
{ "getCar": "http://192.168.1.1:3000/getAllCars" },
{ "getPerson": "http://192.168.1.1:3000/getAllPersons" },
{ "getBook": "http://192.168.1.1:3000/getAllBooks" }
],
Any advice would be appreciated.
You can use map to map your objects to new objects. Those objects have a single property, which you can get with Object.keys. The new object can get that same property name using the computed property name feature:
var obj = {
"Urls": [
{ "getCar": "/getAllCars" },
{ "getPerson": "/getAllPersons" },
{ "getBook": "/getAllBooks" }
],
"ServerUrl": "http://192.168.1.1:3000"
};
var urls = obj.Urls.map(o => Object.keys(o).map(k => ({ [k]: obj.ServerUrl + o[k] }))[0]);
console.log(urls);
const jsonVal = {
"Urls": [
{ "getCar": "/getAllCars" },
{ "getPerson": "/getAllPersons" },
{ "getBook": "/getAllBooks" }
],
"ServerUrl": "http://192.168.1.1:3000"
}
const result = jsonVal.Urls.map(val =>
Object.keys(val).reduce((resultObj, endpointKey) => {
resultObj[endpointKey] = `${jsonVal.ServerUrl}${val[endpointKey]}`;
return resultObj;
}, {})
);
Try (where your data are in d)
d.Urls.forEach( (x,i,a,k=Object.keys(x)[0]) => x[k] = d.ServerUrl + x[k]);
let d = {
"Urls": [
{ "getCar": "/GetAllGroupCustomers" },
{ "getPerson": "/getAllItems" },
{ "getBook": "/GetAllCustomers" }
],
"ServerUrl": "http://192.168.1.1:3000"
}
d.Urls.forEach( (x,i,a,k=Object.keys(x)[0]) => x[k] = d.ServerUrl + x[k]);
console.log(d);
A version that modifies your own object
var obj = {
"Urls": [
{ "getCar": "/getAllCars" },
{ "getPerson": "/getAllPersons" },
{ "getBook": "/getAllBooks" }
],
"ServerUrl": "http://192.168.1.1:3000"
};
obj.Urls.forEach(o => o[Object.keys(o)[0]] = `${obj.ServerUrl}${o[Object.keys(o)[0]]}`);
console.log(obj);

How to simplify javascript nested array with map(), reduce(), and filter()?

First of all I am a newbie into Javascript, ES6 etc and I come from Java background.
I have a complex javascript array structure (example given below),
I am trying to convert this array into a map (similar to how Java has, key value pair kind of thing), key being the permission names (e.g KEY-1, KEY-2, KEY-3, KEY-4,KEY-5 with regards to the javascript array example below) while value being the comma separated values of the actual permission. I can achieve it by looping b thru the nested arrays, but loop is what I am trying to avoid here and wanted to do using map()/reduce()/filter()
Here is an example of how the map should contain the data. Since KEY-2 is present in both the arrays, they will be overridden into one (which is perfectly fine)
KEY-1 = ["Roles.Create","Roles.Edit"]
KEY-2 = ["API-Admin.Create","API-Admin.Edit","API-Admin.Read"]
KEY-3 = ["Roles.Create","Roles.Edit"]
KEY-4 = ["Users.Read"]
KEY-5 = ["Roles.Create","Roles.Edit"]
Javascript Array
const teamArr = [
{
"name":"Team1",
"accountId":"Billing",
"teamId":"12345",
"permissions": {
"KEY-1": [
"Roles.Create",
"Roles.Edit"
],
"KEY-2": [
"API-Admin.Create",
"API-Admin.Edit",
"API-Admin.Read"
],
"KEY-3": [
"Roles.Create",
"Roles.Edit"
]
}
},
{
"name":"Team2",
"accountId":"Sales",
"teamId":"6789",
"permissions": {
"KEY-4": [
"Users.Read"
],
"KEY-2": [
"API-Admin.Create",
"API-Admin.Edit",
"API-Admin.Read"
],
"KEY-5": [
"Roles.Create",
"Roles.Edit"
]
}
}
]
KEY-1, KEY-2,KEY-3,KEY-4, KEY-5 etc are all dynamically generated, so I CAN NOT hardcode these keys names into my code (like KEY-1, KEY-2 etc.)
I followed this post https://www.freecodecamp.org/news/15-useful-javascript-examples-of-map-reduce-and-filter-74cbbb5e0a1f/
and below is what I tried but I guess, I am struggling to properly use sort()/reduce() properly on a complex javascript array. I would prefer plain Javascript/ES6 solution (no JQuery pls).
const sorted = test.sort((a, b) => a.permissions - b.permissions);
// Using reduce:
dict = sorted.reduce(
(dict, el, index) => (dict[el.permissions] = sorted.length - index, dict),
{}
);
console.log(dict)
Any help here would highly be appreciated. Thanks
Since you don't need to worry about the other keys/values, you need to firstly create an array of all the permissions objects. Then you extract the properties from that array, keeping the arrays unique with Sets:
const teamArr = [{"name":"Team1","accountId":"Billing","teamId":"12345","permissions":{"KEY-1":["Roles.Create","Roles.Edit"],"KEY-2":["API-Admin.Create","API-Admin.Edit","API-Admin.Read"],"KEY-3":["Roles.Create","Roles.Edit"]}},{"name":"Team2","accountId":"Sales","teamId":"6789","permissions":{"KEY-4":["Users.Read"],"KEY-2":["API-Admin.Create","API-Admin.Edit","API-Admin.Read"],"KEY-5":["Roles.Create","Roles.Edit"]}}];
const res = teamArr.flatMap(({ permissions }) => Object.entries(permissions)).reduce((a, [k, v]) => ((a[k] = [...new Set(((a[k] = a[k] || []).push(...v), a[k]))], a)), {});
console.log(res);
.as-console-wrapper { max-height: 100% !important; top: auto; }
More verbose version:
const teamArr = [{"name":"Team1","accountId":"Billing","teamId":"12345","permissions":{"KEY-1":["Roles.Create","Roles.Edit"],"KEY-2":["API-Admin.Create","API-Admin.Edit","API-Admin.Read"],"KEY-3":["Roles.Create","Roles.Edit"]}},{"name":"Team2","accountId":"Sales","teamId":"6789","permissions":{"KEY-4":["Users.Read"],"KEY-2":["API-Admin.Create","API-Admin.Edit","API-Admin.Read"],"KEY-5":["Roles.Create","Roles.Edit"]}}];
const permissions = teamArr.map(({ permissions }) => permissions);
const res = permissions.flatMap(Object.entries).reduce((a, [k, v]) => {
a[k] = a[k] || [];
a[k].push(...v);
a[k] = [...new Set(a[k])];
return a;
}, {});
console.log(res);
.as-console-wrapper { max-height: 100% !important; top: auto; }
You can do it with a combination of flatMap and reduce (although flatMap isn't supported on all browsers yet):
const teamArr = [{
"name": "Team1",
"accountId": "Billing",
"teamId": "12345",
"permissions": {
"KEY-1": [
"Roles.Create",
"Roles.Edit"
],
"KEY-2": [
"API-Admin.Create",
"API-Admin.Edit",
"API-Admin.Read"
],
"KEY-3": [
"Roles.Create",
"Roles.Edit"
]
}
},
{
"name": "Team2",
"accountId": "Sales",
"teamId": "6789",
"permissions": {
"KEY-4": [
"Users.Read"
],
"KEY-2": [
"API-Admin.Create",
"API-Admin.Edit",
"API-Admin.Read"
],
"KEY-5": [
"Roles.Create",
"Roles.Edit"
]
}
}
]
const result = teamArr
.flatMap(t => Object.entries(t.permissions))
.reduce((acc, [key, permissions]) => {
acc[key] = acc[key] || [];
acc[key].push(...permissions.filter(p => !acc[key].includes(p)));
return acc;
}, {});
console.log(result);
If the permissions array for a key can be very big, you can also consider using a Set next to each key in the accumulator.
const teamArr = [
{
"name":"Team1",
"accountId":"Billing",
"teamId":"12345",
"permissions": {
"KEY-1": [
"Roles.Create",
"Roles.Edit"
],
"KEY-2": [
"API-Admin.Create",
"API-Admin.Edit",
"API-Admin.Read"
],
"KEY-3": [
"Roles.Create",
"Roles.Edit"
]
}
},
{
"name":"Team2",
"accountId":"Sales",
"teamId":"6789",
"permissions": {
"KEY-4": [
"Users.Read"
],
"KEY-2": [
"API-Admin.Create",
"API-Admin.Edit",
"API-Admin.Read"
],
"KEY-5": [
"Roles.Create",
"Roles.Edit"
]
}
}
];
function extractPermissions(obj) {
const perms = {};
obj.forEach(entry => {
if (!entry['permissions']) {
return;
}
Object.keys(entry.permissions).forEach(key => {
if (!perms[key]) {
perms[key] = [];
}
entry.permissions[key].forEach(value => {
if (!perms[key].some(val => val === value)) {
perms[key].push(value);
}
});
});
});
return perms;
}
console.log(JSON.stringify(extractPermissions(teamArr), null, 2));

lodash filter not finding value in multidimensional array of Shopify order

Looking to see if an order line_item has been refunded before processing...
Here is a single order:
var order = {
line_items: [
{
id: 1326167752753
}
],
refunds: [
{
refund_line_items: [
{
id: 41264152625,
line_item_id: 1326167752753,
}
]
}
]
};
Trying to log out the filter results:
console.log(
_.filter(order, {
refunds: [
{
refund_line_items: [
{
line_item_id: 1326167752753
}
]
}
]
}).length
);
I'm getting 0 on the console.
Am I using _.filter wrong in this case?
Function take needs an array (order is not an array, order.refunds is) and a predicate, not an object.
Anyway, I'd write it using Array.some:
const itemWasRefunded = order.refunds.some(refund =>
refund.refund_line_items.some(refund_line_item =>
refund_line_item.line_item_id === 1326167752753
)
);
Or, alternatively, getting all line_item_ids and checking inclusion:
const itemWasRefunded = _(order.refunds)
.flatMap("refund_line_items")
.map("line_item_id")
.includes(1326167752753);
You can use some and find and do this in lodash and also easily in ES6:
var order = { line_items: [{ id: 1326167752753 }], refunds: [{ refund_line_items: [{ id: 41264152625, line_item_id: 1326167752753, }] }] };
// lodash
const _searchRefunds = (lid) => _.some(order.refunds, x =>
_.find(x.refund_line_items, {line_item_id: lid}))
console.log('loadsh:', _searchRefunds(1326167752753)) // true
console.log('loadsh:', _searchRefunds(132616772323232352753)) // false
//es6
const searchRefunds = (lid) => order.refunds.some(x =>
x.refund_line_items.find(y => y.line_item_id == lid))
console.log('ES6:', searchRefunds(1326167752753)) // true
console.log('ES6:', searchRefunds(132616772323232352753)) // false
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>

What is the most efficient way to merge objects with nested and date keys?

This is an example dataset:
const largeObject = {
"4249":{
"2018-07-25":[
{
"start":"2016-07-25T14:09:20.453Z",
"end":"2016-07-25T14:17:52.147Z"
}
]
},
"9939":{
"2018-07-25":[
{
"start":"2016-07-25T00:50:08.768Z",
"end":"2016-07-25T00:53:16.514Z"
}
]
},
"2149":{
"2018-07-25":[
{
"start":"2016-07-25T00:42:02.569Z",
"end":"2016-07-25T00:43:07.689Z"
}
]
},
"6929":{
"2018-07-24":[
{
"start":"2016-07-24T00:44:30.479Z",
"end":"2016-07-24T00:46:41.315Z"
}
]
},
"7930":{
"2018-07-24":[
{
"start":"2016-07-24T00:39:44.152Z",
"end":"2016-07-24T00:44:05.420Z"
}
]
},
"4796":{
"2018-07-22":[
{
"start":"2016-07-22T12:48:56.169Z",
"end":"2016-07-22T13:38:28.136Z"
}
]
}
}
I am trying to find the most efficient way to get to something like this:
const filteredObject = {
"2018-07-25": [
{
"start":"2016-07-25T14:09:20.453Z",
"end":"2016-07-25T14:17:52.147Z"
}, {
"start":"2016-07-25T00:50:08.768Z",
"end":"2016-07-25T00:53:16.514Z"
},
{
"start":"2016-07-25T00:42:02.569Z",
"end":"2016-07-25T00:43:07.689Z"
}
],
"2018-07-24": [
{
"start":"2016-07-24T00:44:30.479Z",
"end":"2016-07-24T00:46:41.315Z"
},
{
"start":"2016-07-24T00:39:44.152Z",
"end":"2016-07-24T00:44:05.420Z"
}
],
"2018-07-22": [
{
"start":"2016-07-22T12:48:56.169Z",
"end":"2016-07-22T13:38:28.136Z"
}
]
};
So far, I have done:
const filteredObject = {}
const newArr = []
for(key in largeObject){
console.log(largeObject[key])
}
And that gets rid of the random string, but still gets me this:
{ '2018-07-24':
[ { start: '2016-07-24T00:44:30.479Z',
end: '2016-07-24T00:46:41.315Z' } ] }
{ '2018-07-25':
[ { start: '2016-07-25T00:50:08.768Z',
end: '2016-07-25T00:53:16.514Z' } ] }
{ '2018-07-25':
[ { start: '2016-07-25T14:09:20.453Z',
end: '2016-07-25T14:17:52.147Z' } ] }
{ '2018-07-24':
[ { start: '2016-07-24T00:39:44.152Z',
end: '2016-07-24T00:44:05.420Z' } ] }
{ '2018-07-22':
[ { start: '2016-07-22T12:48:56.169Z',
end: '2016-07-22T13:38:28.136Z' } ] }
{ '2018-07-25':
[ { start: '2016-07-25T00:42:02.569Z',
end: '2016-07-25T00:43:07.689Z' } ] }
This is far as I've gotten. I still need to find a way to merge all the arrays with the same key values. It seems like I would need to iterate over this object, keep the date as the key, and push all of the arrays associated with that date-key into one array.
What would be the best way to handle something like this? I also want to do this as efficient as possible without having to iterate over the entire large object each time I check for the date-key and/or push the start/end object into an array of it's own.
You can start with Object.values() of your original data. This will give you an array of the values without the first level keys over which you can reduce(). Then for each of those break it into a key and value. Add the key with an array value if it's not already there and merge in the data.
const largeObject = { "4249":{ "2018-07-25":[ { "start":"2016-07-25T14:09:20.453Z","end":"2016-07-25T14:17:52.147Z"}]},"9939":{ "2018-07-25":[ { "start":"2016-07-25T00:50:08.768Z","end":"2016-07-25T00:53:16.514Z"}]},"2149":{ "2018-07-25":[ { "start":"2016-07-25T00:42:02.569Z","end":"2016-07-25T00:43:07.689Z"}]},"6929":{ "2018-07-24":[ { "start":"2016-07-24T00:44:30.479Z","end":"2016-07-24T00:46:41.315Z"}]},"7930":{ "2018-07-24":[ { "start":"2016-07-24T00:39:44.152Z","end":"2016-07-24T00:44:05.420Z"}]},"4796":{ "2018-07-22":[ { "start":"2016-07-22T12:48:56.169Z","end":"2016-07-22T13:38:28.136Z"}]}}
let filtered = Object.values(largeObject).reduce((a, c) => {
Object.entries(c).forEach(([k, v]) => {
(a[k] || (a[k] = [])).push(...v)
})
return a
},{})
console.log(filtered)

Categories

Resources