I use ajv to validate JSON data model before inserting/updating my database.
Today I want to use this structure:
const dataStructure = {
xxx1234: { mobile: "ios" },
yyy89B: { mobile: "android" }
};
My keys are dynamic because they are ids.
Do you know how to validate it with ajv ?
PS: as an alternative solution, i can of course use this structure:
const dataStructure = {
mobiles: [{
id: xxx1234,
mobile: "ios"
}, {
id: yyy89B,
mobile: "android"
}]
};
Then I would have to loop on the array to find the ids I want.
All my queries will become more complex, it bothers me.
Thank you for your help !
Below example may help you.
1.Validate dynamic key value
Update regex with your requirement.
const dataStructure = {
11: { mobile: "android" },
86: { mobile: "android" }
};
var schema2 = {
"type": "object",
"patternProperties": {
"^[0-9]{2,6}$": { //allow key only `integer` and length between 2 to 6
"type": "object"
}
},
"additionalProperties": false
};
var validate = ajv.compile(schema2);
console.log(validate(dataStructure)); // true
console.log(dataStructure);
2.Validate array of JSON with simple data types.
var schema = {
"properties": {
"mobiles": {
"type": "array", "items": {
"type": "object", "properties": {
"id": { "type": "string" },
"mobile": { "type": "string" }
}
}
}
}
};
const data = {
mobiles: [{
id: 'xxx1234',
mobile: "ios"
}]
};
var validate = ajv.compile(schema);
console.log(validate(data)); // true
console.log(data);
You can add your validation as per requirement.
Related
I get an input like this:
input 1:
{
"name": "Ben",
"description": "Ben",
"attributes": [
{
"type": "Background",
"value": "Default"
},
{
"type": "Hair-color",
"value": "Brown"
}
]
}
input 2
{
"name": "Ice",
"description": "Ice",
"attributes": [
{
"type": "Background",
"value": "Green"
},
{
"type": "Hair-color",
"value": "White"
}
]
}
input 3
{
"name": "Itay",
"description": "Itay",
"attributes": [
{
"type": "Background",
"value": "Default"
},
{
"type": "Hair-color",
"value": "Brown"
}
]
}
What I want to do is count the amount of each type of background and each type of hair-color appearing.
(These are sample examples and in reality there are more types and different values)
Let's say in these examples we have 2 objects that have a background as default then I want to have a count of that like so:
export interface TraitCount {
value: string,
count: number
}
export interface CountOfEachAttribute {
trait_type: string,
trait_count: traitCount[] | null,
total_variations: number
}
I want the most effective code because there are other aspects to the code, in addition it will run on 5-10k queries not just three, so needs
to run in good times too :D
(It's similar to my other question done with python but now I need it in js also)
Atm it's something like this:
(Apart of a much bigger code so keep that in mind)
setInitalCountOfAllAttribute( state, { payload }: PayloadAction<CountOfEachAttribute[] | null> ) {
if (payload === null) {
state.countOfAllAttribute = null;
} else {
state.countOfAllAttribute = payload;
}
},
setCountOfAllAttribute(state, { payload }: PayloadAction<Attribute>) {
if (state.countOfAllAttribute !== null) {
state.countOfAllAttribute.map(
(countOfEachAttribute: CountOfEachAttribute) => {
// Find the trait type
if (countOfEachAttribute.trait_type === payload.trait_type) {
// initiate the trait count array to store all the trait values and add first trait value
if (countOfEachAttribute.trait_count === null) {
const new_trait_count = { value: payload.value, count: 1 };
countOfEachAttribute.trait_count = [new_trait_count];
countOfEachAttribute.total_variations++;
}
// Trait array already existed.
else {
// Check if value already present or not
const checkValue = (obj: any) => obj.value === String(payload.value);
const isPresent = countOfEachAttribute.trait_count.some(checkValue)
const isPresent2 = countOfEachAttribute.trait_count.find((elem: any) => elem.value === String(payload.value))
// Value matched, increase its count by one
if (isPresent2) {
countOfEachAttribute.trait_count &&
countOfEachAttribute.trait_count.map((trait) => {
if (trait.value === payload.value) {
trait.count++;
}
});
}
// Value doesn't match, add a new entry and increase the count of variations by one
else {
const new_trait_count = { value: payload.value, count: 1 };
countOfEachAttribute.trait_count = [
...countOfEachAttribute.trait_count,
new_trait_count,
];
countOfEachAttribute.total_variations++;
}
}
}
}
);
}
},
You can merge all arrays and use Array.reduce.
const input1 = {
"name": "Ben",
"description": "Ben",
"attributes": [{
"type": "Background",
"value": "Default"
},
{
"type": "Hair-color",
"value": "Brown"
}
]
}
const input2 = {
"name": "Ice",
"description": "Ice",
"attributes": [{
"type": "Background",
"value": "Green"
},
{
"type": "Hair-color",
"value": "White"
}
]
}
const input3 = {
"name": "Itay",
"description": "Itay",
"attributes": [{
"type": "Background",
"value": "Default"
},
{
"type": "Hair-color",
"value": "Brown"
}
]
}
const mergedInput = [input1, input2, input3];
const result = mergedInput.reduce((acc, item) => {
item.attributes.forEach(attrItem => {
const existType = acc.find(e => e.trait_type == attrItem.type);
if (existType) {
var existAttr = existType.trait_count.find(e => e.value == attrItem.value);
if (existAttr) {
existAttr.count++;
} else {
existType.trait_count.push({
value: attrItem.value,
count: 1
});
existType.total_variations++;
}
} else {
acc.push({
trait_type: attrItem.type,
trait_count: [{
value: attrItem.value,
count: 1
}],
total_variations: 1
})
}
});
return acc;
}, []);
console.log(result);
I suggest instead of creating an array for trait_count to make it an object so you don't have to iterate over it whenever you are adding a new attribute. In the snippet below I'm using the value of the attribute as a sort of hash that allows the access to the given property without having to call the Array.prototype.find function
const input1 = {"name":"Ben","description":"Ben","attributes":[{"type":"Background","value":"Default"},{"type":"Hair-color","value":"Brown"}]};
const input2 = {"name":"Ice","description":"Ice","attributes":[{"type":"Background","value":"Green"},{"type":"Hair-color","value":"White"}]};
const input3 = {"name":"Itay","description":"Itay","attributes":[{"type":"Background","value":"Default"},{"type":"Hair-color","value":"Brown"}]};
function countAtributes(input, totalCounts={}) {
input.attributes.forEach((attribute) => {
if (!totalCounts[attribute.type])
totalCounts[attribute.type] = {trait_type: attribute.type, trait_count: {}, total_variations: 0};
if (!totalCounts[attribute.type].trait_count[attribute.value]) {
totalCounts[attribute.type].trait_count[attribute.value] = {value: attribute.value, count: 1};
totalCounts[attribute.type].total_variations+=1;
}
else totalCounts[attribute.type].trait_count[attribute.value].count +=1;
})
}
const totalCounts = {};
countAtributes(input1, totalCounts);
countAtributes(input2, totalCounts);
countAtributes(input3, totalCounts);
console.log(totalCounts);
It could be turned into the array afterwards with Object.values if necessary
I believe it is a much better approach to what you had before as you don't have to iterate over the tables of trait_counts. In theory it should significantly reduce the time taken. Iterating over the array and checking a condition each time is much slower than key lookup in Javascript object
I have the following json schema.
const myJson = {
"type": "typeName"
"firstName": "Steven",
"lastName": "Smith",
"address": {
"primary": {
"city": "abc",
"street": {
"name": {
"subName": "someName"
}
}
}
}
}
And I want to loop over each of the properties for required validation on this json, I have the following code so far which works if the property in the json is not nested.
let errors = [];
const typeName = ['firstName', 'lastName'],
const typeAttr = Object.keys(myJson);
typeName.forEach(attr => {
if (!typeAttr.includes(attr)) {
errors.push(`Missing field: ${attr}`);
}
});
How can I add the nested json property like primary, city, street and validate the way I have done it.
Here's how you can check nested properties on json object
const myJson = {
type: "typeName",
firstName: "Steven",
lastName: "Smith",
address: {
primary: {
city: "abc",
street: {
name: {
subName: "someName",
},
},
},
},
};
let errors = [];
const typeName = ["firstName", "lastName", "address.primary", "address.primary.city"];
function checkNested(obj, level, ...rest) {
if (obj === undefined) return false;
if (rest.length == 0 && obj.hasOwnProperty(level)) return true;
return checkNested(obj[level], ...rest);
}
typeName.forEach((attr) => {
let props = attr.split(".");
if (!checkNested(myJson, ...props)) {
errors.push(`Missing field: ${attr}`);
}
});
console.log(errors);
For reference on how to check Nested properties you can check this answer.
Test for existence of nested JavaScript object key
I would do something like this. This method gives whether the data is having all the provided keys or not i.e., will return either true or false
let obj = {"type":"typeName","firstName":"Steven","lastName":"Smith","address":{"primary":{"city":"abc","street":{"name":{"subName":"someName"}}}}};
const typeName = ['firstName', 'lastName', 'address', 'address.primary', 'address.primary.city', 'address.primary.street'];
const validate = (data, types) => {
return types.every(type => {
// Split the keys using `.`
const keys = type.split('.');
// Check if the length is more than 1,
// if yes, then we need to check deeply
if (keys.length > 1) {
let datum = {
...data
};
// Check through all the keys found using split
for (let key of keys) {
// if any key is not found or falsy then return false
if (!datum[key]) {
return false;
}
datum = datum[key];
}
return true;
} else {
// if the length of the keys is not more than 1 then it means
// the key is at the top level and return the value as boolean
return !!data[type]
}
})
}
console.log(validate(obj, typeName));
console.log(validate(obj, ['firstName', 'lastName', 'address', 'address.primary', 'address.primary.zip']));
This below method will return the keys that were not present in the provided data
const validate = (data, types) => {
let errors = [];
types.forEach(type => {
const keys = type.split('.');
let datum = {
...data
};
// Loop through the keys
for (let [index, key] of keys.entries()) {
// Check if the key is not available in the data
// then push the corresponding key to the errors array
// and break the loop
if (!datum[key]) {
errors.push(keys.slice(0, index + 1).join('.'));
break;
}
datum = datum[key];
}
})
return errors;
}
const obj = {"type":"typeName","firstName":"Steven","lastName":"Smith","address":{"primary":{"city":"abc","street":{"name":{"subName":"someName"}}}}};
const typeName = ['firstName', 'lastName', 'address', 'address.primary', 'address.primary.city', 'address.primary.street'];
console.log(validate(obj, ['firstName', 'lastName']));
console.log(validate(obj, ['firstName', 'lastName', 'address', 'address.primary', 'address.primary.zip']));
console.log(validate(obj, [...typeName, 'test', 'address.primary.zip', 'address.test.zip']));
Use JSON Schema, a proposed IETF standard with tons of library implementations available for several languages for describing, generating, and validating JSON documents.
To require your JSON to have all the properties be present, you'll need a JSON Schema like below.
let typeNameSchema = {
"$schema": "http://json-schema.org/draft-07/schema",
"$id": "https://example.com/typename.schema.json",
"title": "Type Name",
"description": "A person's name and address details",
"type": "object",
"required": [
"firstName",
"lastName",
"address"
],
"properties": {
"type": {
"type": "string"
},
"firstName": {
"type": "string"
},
"lastName": {
"type": "string"
},
"address": {
"type": "object",
"required": [
"primary"
],
"properties": {
"primary": {
"type": "object",
"required": [
"city",
"street"
],
"properties": {
"city": {
"type": "string"
},
"street": {
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"type": "object",
"required": [
"subName"
],
"properties": {
"subName": {
"type": "string"
}
}
}
}
}
}
}
}
}
}
}
Then use any library to validate your JSON data against the above schema. jsonschema is one of the most popular JSON Schema validators for JavaScript. Here's how to validate your myJson data against the above typeNameSchema.
const Validator = require('jsonschema').Validator;
const validator = new Validator();
console.log(validator.validate(myJson, typeNameSchema)); // result
Validation results will be returned in a ValidatorResult object with the most important properties valid of type boolean and errors of type ValidationError[]. ValidationError object will have the property name and error message.
I have a json request input below and would want to construct a specific json format that the backend needs using javascript. What is the best way or steps to construct them? Any help would be appreciated! Thanks.
Request input :
{
"Product": "abc"'
"Data": "{"Name":"John","Email":"john#example.com"}"
}
Request output to the backend as follow:
{
"variables": {
"Product": {
"value": "abc",
"type": "string"
},
"Data": {
"value": "{"Name":"John","Email":"john#example.com"}",
"type": "string"
}
},
"Key": "123"
}
Thanks in advance
You can just map over all the entries in your object.
Documentation: Object.entries() Array.reduce()
const input = {
Product: "abc",
Data: "{\"Name\":\"John\",\"Email\":\"john#example.com\"}"
};
const variables = Object.entries(input).reduce((output, [key, value]) => {
output[key] = {
type: typeof value,
value
};
return output;
}, {});
const result = {
variables,
key: '123'
};
console.log(result);
In Javascript, how to retrieve an object in an array by one of its property ?
Hi all,
let's assume that we have the below :
"Attributes":[
{
"Name":"Brief",
"Value":"This skirt was fabriced from ...."
},
{
"Name":"Details",
"Value":"Measurements and Pictures are real"
},
{
"Name":"SKUNumber",
"Value":"12345678"
}
]
What I need to do is to get the value of "Value" based on "Name"..
For example :
console.log(Attributes.Brief) ==> "This skirt was fabriced from ...."
So I need a function to help doing that
Note that I don't want to use the index of the object, because its order may changed.
Thank you
Well, it's always better to show what you have attempted rather than just asking..
You can use Array.find to achieve this
let Attributes = [
{
"Name":"Brief",
"Value":"This skirt was fabriced from ...."
},
{
"Name":"Details",
"Value":"Measurements and Pictures are real"
},
{
"Name":"SKUNumber",
"Value":"12345678"
}
]
function getValueByName(name) {
return Attributes.find(d => d.Name.toLowerCase() == name.toLowerCase()).Value
}
console.log(getValueByName('Brief'))
console.log(getValueByName('details'))
console.log(getValueByName('SKUNumber'))
One option you have is to use Array.prototype.filter:
const d = [{
"Name": "Brief",
"Value": "This skirt was fabriced from ...."
},
{
"Name": "Details",
"Value": "Measurements and Pictures are real"
},
{
"Name": "SKUNumber",
"Value": "12345678"
}
]
console.log(d.filter(x=>x.Name==="Brief")[0].Value)
You can also make it more generic:
const d = [{
"Name": "Brief",
"Value": "This skirt was fabriced from ...."
},
{
"Name": "Details",
"Value": "Measurements and Pictures are real"
},
{
"Name": "SKUNumber",
"Value": "12345678"
}
]
const getValOfXfromArrByValOfY = (arr, x, y, val) => arr.find(z => z[y] === val)[x]
console.log(getValOfXfromArrByValOfY(d, 'Value', 'Name', 'SKUNumber'))
You could use a Proxy with a getter for the key, which returns a find of the object with the value.
var object = { attributes: [{ Name: "Brief", Value: "This skirt was fabriced from ...." }, { Name: "Details", Value: "Measurements and Pictures are real" }, { Name: "SKUNumber", Value: "12345678" }] },
attributes = new Proxy(
object.attributes,
{ get: (array, prop) => (array.find(({ Name }) => Name === prop) || {}).Value }
);
console.log(attributes.Brief);
console.log(attributes.SKUNumber);
You can use javascript find function see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find see bellow sample code:
var Attributes =[
{
"Name":"Brief",
"Value":"This skirt was fabriced from ...."
},
{
"Name":"Details",
"Value":"Measurements and Pictures are real"
},
{
"Name":"SKUNumber",
"Value":"12345678"
}
]
var found = Attributes.find(function(element) {
return element.Name == "Details";
});
console.log(found.Value); //output : Measurements and Pictures are real
Actual Output :
{
"workbookInformation": {
...
},
"dashboard1": {
"auto-hidden": "0",
"maximized": "1",
"worksheet": {
"worksheet8": "APAC",
"worksheet2": "Freq_SC_Value",
"worksheet3": "L1 Type Filter",
"worksheet1": "Pillar Name"
},
"class": "dashboard",
"name": "AnalysisByGeography"
},
"dashboard2": {
...
},
"datasources3": {
...
}
}
Required Output :
{
"workbookInformation": {
...
},
"AnalysisByGeography": {
"auto-hidden": "0",
"maximized": "1",
"worksheet": {
"APAC": "worksheet8",
"Freq_SC_Value": "worksheet2",
"L1 Type Filter": "worksheet3",
"Pillar Name": "worksheet1"
},
"class": "dashboard",
"name": "AnalysisByGeography"
},
"dashboard2": {
...
},
"datasources3": {
...
}
}
I tried with code which gives key name . i tried "//jashkenas.github.io/underscore/underscore-min.js" for swapping the keys. I tried external JS file for reversing the key value pair.
Try to use JSON.stringify(youredata). Otherwise maybe u need to split the given data and convert the array with the same function to a json-type.
Okay, here I wrote for you how to do it, because I don't see more how to explain it. But I strongly suggest, that if you begin in JavaScript, to learn more before asking those type of questions.
Maybe other validations may be needed. Also, I did it only for keys starting with "dashboard", even though I thought it would probably be for all keys having a string plus a number after then and containing an object that has a key "name" in it. If you want to learn, you shall fix that by yourself as you have a pretty strong base here.
Hoping you'll have find learning as I had and still have!
var data =
{
"workbookInformation": {
"a": "..."
},
"dashboard1": {
"auto-hidden": "0",
"maximized": "1",
"worksheet": {
"worksheet8": "APAC",
"worksheet2": "Freq_SC_Value",
"worksheet3": "L1 Type Filter",
"worksheet1": "Pillar Name"
},
"class": "dashboard",
"name": "AnalysisByGeography"
},
"dashboard2": {
"a": "..."
},
"datasources3": {
"a": "..."
}
}
var isDashboard = new RegExp('dashboard[0-9]+', 'i');
var isWorksheet = new RegExp('worksheet[0-9]+', 'i');
for(var dataKey in data) {
var innerData = data[dataKey];
if (typeof(innerData["worksheet"]) == "object") {
var newWS = {};
for(var wsKey in innerData["worksheet"]) {
newWS[innerData["worksheet"][wsKey]] = wsKey;
}
innerData["worksheet"] = newWS;
}
if (isDashboard.test(dataKey) && innerData['name']) {
delete data[dataKey];
dataKey = innerData['name'];
}
data[dataKey] = innerData;
}
console.log(data);