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')))
Related
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()
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);
I am having json template for elasticsearch query:
var getTemplate = function (agg, filter_term) {
var template = {
"size": 0,
track_total_hits: true,
query: {
bool: {
must: [
{
"query_string": {
"query": filter_term
}
},
aggs: {
"nested" : { "value_count" : { "field" : agg } },
agg: {
terms: {
field: agg,
size: 10,
order: {
"_count": "desc"
}
}
}
}
};
return template;
Sometimes, I want to skip the block with query_string so I want to pass in in the function call. So instead of this:
const callTerminated = agg_filter.getTemplate( 'bbbbb', 'aaa');
Do that:
const callTerminated = agg_filter.getTemplate( 'bbbbb', {"query_string": {"query": aaa }});
Or:
const callTerminated = agg_filter.getTemplate( 'bbbbb', "");
But how to change the template query? It wants comma after variable but when I skip query_string, I don't need comma.
New json template:
var getTemplate = function (agg, filter_term) {
var template = {
"size": 0,
track_total_hits: true,
query: {
bool: {
must: [
filter_term //here it wants comma
aggs: {
"nested" : { "value_count" : { "field" : agg } },
agg: {
terms: {
field: agg,
size: 10,
order: {
"_count": "desc"
}
}
}
}
};
return template;
So how to do it?
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);
})
I am new to nodejs, right now I am able to fetch the data from the database and display as REST API which is of key/value pair. My output looks like as shown below
[{"id":"793","actionname":"test_bsc_interface","actionid":"100"}].
But I need to format as
{ "header" : [id,actionname,actionid],
"values" : [793 ,test_bsc_interface,100] ],}.
I tried with JSON.stringfy and checked few website but it's not working.
Could any one please help me through which link I should go through or approach.
The easy way to do it is to use underscore or lodash module to format it:
var _ = require('lodash');
var data = [{"id":"793","actionname":"test_bsc_interface","actionid":"100"}];
var result = _.map(data, function(o) {
return {headers: _.keys(o), values : _.values(o)}
});
console.dir(result);
Output is:
[
{
headers: [ 'id', 'actionname', 'actionid' ],
values: [ '793', 'test_bsc_interface', '100' ]
}
]
To get the result you want, just do result[0]
Note that I use _.map() instead of data[0] because this will work for the array (your array result from the query) that has more than 1 item.
Revised answer according to your comment (1 header line and all value lines in an array):
var _ = require('lodash');
var data = [
{ id: '714', actionname: 'test_bsc_interface', actionid: '100' },
{ id: '715', actionname: 'list_all_available_interfaces', actionid: '103' },
{ id: '716', actionname: 'check_tt', actionid: '101' }
];
var result;
if (_.size(data) > 0) {
result = {
headers: _.keys(data[0]),
values: _.map(data, function(o) {
return _.values(o);
})
};
}
console.dir(result);
and the output is:
{
headers: [ 'id', 'actionname', 'actionid' ],
values: [
[ '714', 'test_bsc_interface', '100' ],
[ '715', 'list_all_available_interfaces', '103' ],
[ '716', 'check_tt', '101' ]
]
}