json nested output from joining data from 2 google sheets - json

Here is the scenerio:
i have 2 google sheets that have related data
Orders
OrderNum
CustName
1000
Cust 1
1001
Cust 2
Details
OrderNum
ItemNum
SerNum
1000
Item 1
12345
1000
Item 2
23456
1000
Item 3
34567
1001
Item 1
45678
1001
Item 2
56789
I need to output to a JSON file that looks like this - I will need to transmit this file to to an API via curl or similar method. Help... I am a total newb to this and am looking for guidance...
{
"Orders" : [
{
"CustName" : "Cust 1",
"OrderNum" : "1000",
"Items" : [
{
"ItemNum" : "Item1",
"SerNum" : "12345",
"task_id" : 0
},
{
"ItemNum" : "Item2",
"SerNum" : "23456",
"task_id" : 1
},
{
"ItemNum" : "Item3",
"SerNum" : "34567",
"task_id" : 2
],
},
{
"CustName" : "Cust 2",
"OrderNum" : "1001",
"Items" : [
{
"ItemNum" : "Item1",
"SerNum" : "45678",
"task_id" : 0
},
{
{
"ItemNum" : "Item2",
"SerNum" : "56789",
"task_id" : 1
],
}
Thanks for help...
Since I am new to this I did not even know where to start...
Data is in google sheets...

In your situation, how about using a Google Apps Script as follows?
Sample script:
Please copy and paste the following script to the script editor of Spreadsheet, and please set the sheet names. And, run the function. By this, you can see the result values in the log.
function myFunction() {
const sheetName1 = "Orders";
const sheetName2 = "Details";
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet1 = ss.getSheetByName(sheetName1);
const sheet2 = ss.getSheetByName(sheetName2);
const values1 = sheet1.getRange("A2:B" + sheet1.getLastRow()).getValues();
const values2 = sheet2.getRange("A2:C" + sheet2.getLastRow()).getValues();
const obj2 = values2.reduce((o, [a, b, c]) => {
const temp = { "ItemNum": b, "SerNum": c, "task_id": o[a] ? o[a].length : 0 };
return Object.assign(o, { [a]: (o[a] ? [...o[a], temp] : [temp]) });
}, {});
const res = {
"Orders": values1.reduce((ar, [a, b]) => {
if (obj2[a]) {
ar.push({ "CustName": b, "OrderNum": a, "Items": obj2[a] });
}
return ar;
}, [])
};
console.log(res);
}
When this script is run using your provided sample tables, the value of res is as follows.
{
"Orders":[
{
"CustName":"Cust 1",
"OrderNum":1000,
"Items":[
{
"ItemNum":"Item 1",
"SerNum":12345,
"task_id":0
},
{
"ItemNum":"Item 2",
"SerNum":23456,
"task_id":1
},
{
"ItemNum":"Item 3",
"SerNum":34567,
"task_id":2
}
]
},
{
"CustName":"Cust 2",
"OrderNum":1001,
"Items":[
{
"ItemNum":"Item 1",
"SerNum":45678,
"task_id":0
},
{
"ItemNum":"Item 2",
"SerNum":56789,
"task_id":1
}
]
}
]
}
Note:
If you want to use this script as a custom function, please use the following script. In this case, please put a custom function like =SAMPLE(Orders!A2:B, Details!A2:C) to a cell.
function SAMPLE(values1, values2) {
const obj2 = values2.reduce((o, [a, b, c]) => {
if (a && b && c) {
const temp = { "ItemNum": b, "SerNum": c, "task_id": o[a] ? o[a].length : 0 };
return Object.assign(o, { [a]: (o[a] ? [...o[a], temp] : [temp]) });
}
return o;
}, {});
const res = {
"Orders": values1.reduce((ar, [a, b]) => {
if (obj2[a]) {
ar.push({ "CustName": b, "OrderNum": a, "Items": obj2[a] });
}
return ar;
}, [])
};
return JSON.stringify(res);
}
References:
reduce()
map()

Related

JSON array item validation

I'd like to have tooling to perform certain validations on JSON. Explanation with examples:
Given JSON fragment:
{
"optionsMinValue": 0
"optionsMaxValue": 56
"options": [
{
"name": "name1",
"value": 0
},
{
"name": "name2",
"value": 1
},
{
"name": "name3",
"value": 56
}
]
}
Validation examples:
Given the fragment above, the validation of optionsMaxValue should
pass.
Given the fragment above, if optionsMaxValue is changed to 55, then
the validation should fail.
Added bonus validation:
Check whether an item is included in the options array for every integer between optionsMinValue and optionsMaxValue. In other words, in the given fragment the array should contain 57 items with an item for each value from 0 to 56.
Existing tooling:
Does tooling exist that can be used relatively easily to perform these sorts of checks?
First thought is that something like json-schema validation could be done. It has been a few years since I looked at that as an option, so my hope is that tooling has emerged that is a homerun on this.
Ajv JSON schema validator - github link
const schema = {
type: "object",
properties: {
name: {type: "string"},
value: {type: "number", minimum: 0, maximum: 55},
},
required: ["name", "value"],
additionalProperties: false,
}
const option = {
"name": "name1",
"value": 0
},
const validate = ajv.compile(schema)
const valid = validate(data)
if (!valid) console.log(validate.errors)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ajv/4.4.0/ajv.min.js"></script>
Joi package is best for these kind of validations
following Joi schema can be used to solve your requirement
Joi.object({
optionsMinValue: Joi.number().min(0).max(30).required(),
optionsMaxValue: Joi.number().min(56).max(100).required(),
options: Joi.array().items(
Joi.object({
name: Joi.string().required(),
value: Joi.number().min(0).max(56).required(),
})
),
});
Following is a sample code that works for your scenario
const inputData = {
optionsMinValue: 0,
optionsMaxValue: 56,
options: [
{
name: "name1",
value: 0,
},
{
name: "name2",
value: 1,
},
{
name: "name3",
value: 56,
},
],
};
const Joi = joi; // for node.js use - const Joi = require("joi");
// Schema for validation
const schema = Joi.object({
optionsMinValue: Joi.number().min(0).max(30).required(),
optionsMaxValue: Joi.number().min(56).max(100).required(),
options: Joi.array().items(
Joi.object({
name: Joi.string().required(),
value: Joi.number().min(0).max(56).required(),
})
),
});
const runValidation = (schema, inputData) => {
const validationResult = Joi.compile(schema)
.prefs({ errors: { label: "key" }, abortEarly: false })
.validate(inputData);
if (validationResult.error) {
// Validation failed
console.log("Error, validation failed");
// Set error message to string
const errorMessage = validationResult.error.details
.map((details) => details.message)
.join(", ");
console.log("failure reason - ", errorMessage);
return;
}
console.log("validation passed");
};
runValidation(schema, inputData);
<script src="https://cdn.jsdelivr.net/npm/joi#17.6.0/dist/joi-browser.min.js"></script>
Even if you use an existing tool, you should write validation rules for that tool. Since you are not an expert in any of these tools, it may be easier to write a few lines of code in your preferred language. For example, in JavaScript it might look like this:
function validateJson(jsonToValidate, maxValue = 56) {
if (jsonToValidate.optionsMaxValue !== maxValue) {
console.log("Failure on optionsMaxValue.");
return false;
}
if (jsonToValidate.options.length !== maxValue+1) {
console.log("Incorrect number of items.");
return false;
}
let values = jsonToValidate.options.map(a => a.value).sort();
if (values[0] !== 0 || values[maxValue] !== maxValue) {
console.log("Values out of desired sequence.");
return false;
}
let sum = values.reduce((a, b) => a + b, 0);
if (sum !== maxValue * (maxValue + 1) / 2) {
console.log("Values out of desired sequence.");
return false;
}
console.log("Validation PASSED.");
return true;
}
Let's try with truncated json object:
let jsonSample = {
"optionsMinValue": 0,
"optionsMaxValue": 2,
"options": [{
"name": "name1",
"value": 0
},
{
"name": "name2",
"value": 1
},
{
"name": "name3",
"value": 2
}
]
};
function validateJson(jsonToValidate, maxValue = 56) {
if (jsonToValidate.optionsMaxValue !== maxValue) {
console.log("Failure on optionsMaxValue.");
return false;
}
if (jsonToValidate.options.length !== maxValue+1) {
console.log("Incorrect number of items.");
return false;
}
let values = jsonToValidate.options.map(a => a.value).sort();
if (values[0] !== 0 || values[maxValue] !== maxValue) {
console.log("Values out of desired sequence.");
return false;
}
let sum = values.reduce((a, b) => a + b, 0);
if (sum !== maxValue * (maxValue + 1) / 2) {
console.log("Values out of desired sequence.");
return false;
}
console.log("Validation PASSED.");
return true;
}
validateJson(jsonSample, 2);

add a created contact to a group by people api using google apps script

I need to create a new group "Carpenters" (if the group doesn't exist) and add the created contact to "Carpenters" group
I have tried with
function doGet(e) {
var id = People.People.createContact(
{
"names": [
{
"displayNameLastFirst": "Smith Jefferson Jones",
"familyName": "Jones",
}
],
/* "phoneNumbers": [
{
'value': "+12345679962"
}
],
"emailAddresses": [
{
'value': ' '
}
]*/
}
).metadata.sources[0].id;
return ContentService.createTextOutput("Success");
}
You could do the following:
Retrieve the resourceName of the created contact (to be used on step 4).
Check if the group exists by listing all contactGroups and looking for a group whose name is Carpenters (using find()).
Create a contactGroup called Carpenters if it doesn't exist, using contactGroups.create.
Use contactGroups.members.modify to add the created contact to the group.
Code sample:
function doGet(e) {
// 1. CREATE CONTACT:
var contactResource = {
"names": [{
"displayNameLastFirst": "Smith Jefferson Jones",
"familyName": "Jones",
}],
/* "phoneNumbers": [{
'value': "+12345679962"
}],
"emailAddresses": [{
'value': ' '
}]*/
}
var contactResourceName = People.People.createContact(contactResource)["resourceName"];
// 2. CHECK IF GROUP EXISTS:
var groupName = "Carpenters";
var groups = People.ContactGroups.list()["contactGroups"];
var group = groups.find(group => group["name"] === groupName);
// 3. CREATE GROUP IF DOESN'T EXIST:
if (!group) {
var groupResource = {
contactGroup: {
name: groupName
}
}
group = People.ContactGroups.create(groupResource);
}
var groupResourceName = group["resourceName"];
// 4. ADD CONTACT TO GROUP:
var membersResource = {
"resourceNamesToAdd": [
contactResourceName
]
}
People.ContactGroups.Members.modify(membersResource, groupResourceName);
return ContentService.createTextOutput("Success");
}
Reference:
contactGroups.members.modify

How to export the api call results to a csv with the values of single response in one row?

My api response looks like below
[
{
"What time is it?": [
"option_2"
]
},
{
"When will we go home?": [
"option_1"
]
},
{
"When is your birthday?": [
"2050"
]
},
{
"How much do you sleep?": [
"Ajajajsjiskskskskdkdj"
]
}
],
[
{
"What time is it?": [
"option_2"
]
},
{
"When will we go home?": [
"option_1"
]
},
{
"When is your birthday?": [
"10181"
]
},
{
"How much do you sleep?": [
"Ajskossooskdncpqpqpwkdkdkskkskskksksksksks"
]
}
]
Now in react, I want to export the results to a csv. I can do it by export-to-csv but the formatting is the issue here. I want the values of each question of a single response in one row under their labels(questions). So if I have two response like above I want to have export it in two rows, not 8 as there are 8 total questions.
Here is how I want it to get exported.
I have tried so far like this but no luck.
this is my export data function
exp =()=>{
const raw = []
console.log(this.state.data[0].sbm_id)
axios.get(`/dashboard/${this.props.proj_id}/whole_sub/`)
.then(res=>{
// console.log('1')
// console.log(res.data[0][0])
// console.log('2')
for (let i =0;i<this.state.data.length;i++){
for(let j = 0;j<res.data[0].length;j++){
// let sub=[]
//res.data[i][j].ID = this.state.data[i].sbm_id
raw.push(res.data[i][j])
}
}
}
)
let curr = this.state
curr.exp = raw
this.setState({exp:curr.exp})
}
Here is my export function
rawExport=()=>{
const csvExporter = new ExportToCsv(optionsExp);
csvExporter.generateCsv(this.state.exp);
}
First step is to flatten the initial nested array to get a homogeneously shaped array, then you keep on reducing it further.
const data = [
[
{
"What time is it?": [
"option_2"
]
},
{
"When will we go home?": [
"option_1"
]
},
{
"When is your birthday?": [
"2050"
]
},
{
"How much do you sleep?": [
"Ajajajsjiskskskskdkdj"
]
}
],
[
{
"What time is it?": [
"option_2"
]
},
{
"When will we go home?": [
"option_1"
]
},
{
"When is your birthday?": [
"10181"
]
},
{
"How much do you sleep?": [
"Ajskossooskdncpqpqpwkdkdkskkskskksksksksks"
]
}
]
];
const flattenArray = (arr) => [].concat.apply([], arr);
// Flatten the initial array
const flattenedArray = flattenArray(data);
// Keep on reducing the flattened array into an object
var res = flattenedArray.reduce((acc, curr) => {
const [key, val] = flattenArray(Object.entries(curr));
if (!acc[key]) {
acc[key] = [].concat(val);
} else {
val.forEach(x => {
if (!acc[key].includes(x)) {
acc[key].push(x);
}
});
}
return acc;
}, {});
console.log(res);

How to access nested data in json string in typescript

I have this json structure an can't find a way to access the data values(data1, data2 and date), i'd like to have those values in an array than i can sort by date:
{
"07" : {
"07" : {
"data1" : "-1",
"data2" : "test",
"date" : "1995-07-07"
},
"08" : {
"data1" : "1",
"data2" : "test",
"date" : "1995-07-08"
},
"09" : {
"data1" : "-1",
"data2" : "test",
"date" : "1995-07-09"
},
"10" : {
"data1" : "-1",
"data2" : "test",
"date" : "1995-07-10"
}
},
"08" : {
"07" : {
"data1" : "1",
"data2" : "test",
"date" : "1995-08-07"
},
"08" : {
"data1" : "1",
"data2" : "test",
"date" : "1995-08-08"
},
"09" : {
"data1" : "1",
"data2" : "test",
"date" : "1995-08-09"
}
}
}
Because my keys aren't defined as constant i don't know what they'll be in advance.
Polyfill for Object.entries:
const reduce = Function.bind.call(Function.call, Array.prototype.reduce);
const isEnumerable = Function.bind.call(Function.call, Object.prototype.propertyIsEnumerable);
const concat = Function.bind.call(Function.call, Array.prototype.concat);
const keys = Reflect.ownKeys;
if (!Object.values) {
Object.values = function values(O) {
return reduce(keys(O), (v, k) => concat(v, typeof k === 'string' && isEnumerable(O, k) ? [O[k]] : []), []);
};
}
if (!Object.entries) {
Object.entries = function entries(O) {
return reduce(keys(O), (e, k) => concat(e, typeof k === 'string' && isEnumerable(O, k) ? [[k, O[k]]] : []), []);
};
}
Code:
for (const [key, value] of Object.entries(myObject))
{
for (const [key2, value2] of Object.entries(value))
{
value2.data1;
value2.data2;
value2.date;
}
}
Instead Object.entries you can enumerate object like this.
for (var key in myObject)
{
for (var key2 in myObject[key])
{
myObject[key][key2].data1;
myObject[key][key2].data2;
myObject[key][key2].date;
}
}
You can get all the key names from your json as an Array by calling the method:
keys = Object.getOwnPropertyNames(jsonObj);
In your example this will return an array ['07', '08'] to get the actual objects from the name you can call:
keys.forEach((key) => {
objects = Object.getOwnPropertyDescriptor(jsonObj, key)
})
And then you can find the names of the keys of these objects and repeat
objects.forEach((object) => {
keys = Object.getOwnPropertyNames(object);
})

Reorder JSON by value, showing a,b,c,a,b,c

I have some JSON that is in the below format. I want to have a script that rearranges this so that it's ordered by preference, but with alternating values. For instance, showing a,b,c,a,b,c. Can anyone help with this?
[
{
"name" : "Tim",
"preference" : "b"
},
{
"name" : "Tom",
"preference" : "b"
},
{
"name" : "Steve",
"preference" : "a"
},
{
"name" : "Rick",
"preference" : "a"
},
{
"name" : "Nile",
"preference" : "c"
},
{
"name" : "James",
"preference" : "c"
}
]
Underscore provides utilities suited to this task.
First, group your input by the preference field:
var groups = _.groupBy(input, 'preference');
Then convert this into an array of arrays:
var arrays = _.values(groups);
Then, "zip" the groups:
var result = _.zip.apply(null, arrays);
In one line:
var result = _.zip.apply(null, _.values(_.groupBy(input, 'preference')));
Non-Underscore version
If you can't/don't want to use Underscore, then you'll have to write your own versions of groupBy and zip:
function groupBy(array, prop) {
var result = {};
for (var i = 0; i < array.length; i++) {
var entry = array[i];
var val = entry[prop];
if (!result[val]) result[val] = [];
result[val].push(entry);
}
return result;
}
Either this or Underscore's _.groupBy will transform your input into
{
b: [ { name: 'Tim', preference: 'b' }, ... ],
a: [ { name: 'Rick', preference: 'a' }, ... ]
}
To get an array of the arrays:
function values(obj) {
return Object.keys(obj) . sort() . map(function(key) { return obj[key]; });
}
This will result in
[
[ { name: 'Tim', preference: 'b' }, ... ],
[ { name: 'Rick', preference: 'a' }, ... ]
]
Then for zip:
function zip(arrays) {
var result = [];
var n = 0;
var more = true;
var array;
while (more) {
more = false;
for (var i = 0; i < arrays.length; i++) {
array = arrays[i];
if (n < array.length) {
more = true;
result.push(array[n]);
}
}
n++;
}
return result;
}
Note: this implementation of zip takes an array of arrays as a parameter, unlike Underscore's version, which takes the arrays as individual parameters.
Then
zip(values(groupBy(input, 'preference')))