JSON Schema "oneOf" each? - javascript

Is it possible to make a JSON Schema which validates that an array contains at least 1 instance of three non-overlapping types. A simple example is an array of numbers of any length that contains at least one 1, one 2, and one 3.
I can't use allOf because that will validate that every number in the array is a 1, 2, and 3 - which no number can be 😅
I can't use anyOf because then I can have an array with only one element 😢
I can't use oneOf because, again, I can have an array with one number
(...also I'm unclear if I should be working with the contains key or inside the items key)
What I'm looking for is a way to say "one of each" of these items. Is it possible with JSON Schema?
Here's a broken example with oneOf
{
"type": "object",
"properties": {
"numbers": {
"type": "array",
"contains": {
"oneOf": [
{
"const": 1
},
{
"const": 2
},
{
"const": 3
}
]
},
"items": {
"type": "number"
}
}
}
}
[1] – Should NOT validate
[1,2] - Should NOT validate
[1,2,3] - SHOULD validate
[1,2,3,4] - SHOULD validate

You can put the contains inside of an allOf:
{
"type": "object",
"properties": {
"numbers": {
"type": "array",
"allOf": [
{
"contains": {
"const": 1
}
},
{
"contains": {
"const": 2
}
},
{
"contains": {
"const": 3
}
}
]
}
}
}``

Related

How to generate dynamically two arrays at same time with Formik

I working in a form who generate fields dynamically using formik with the <FieldArray />that acts as a functional component.
So, I have this object to send on submit to my backend:
{
"tapes": [
{
"name": "",
"elements": [
{
"propsOne": "",
"propsTwo": "",
"view": 0,
}
]
}
],
"typeOptions": []
}
As you can see, I have two arrays to working in this json object, one is Tapes who has Elements to work together and typeOptions who has values to send. My proposal is to send in a form with dynamic fields to my backend when whenever the user add new tapes and add new field props which will be rendered later in a mobile app.
Assuming the user added two new tapes and added two new elements for each one, the result of this json would be this:
{
"tapes": [
{
"name": "TapeOne",
"elements": [
{
"propsOne": one,
"propsTwo": two,
"view": 0,
},
{
"propsOne": 1,
"propsTwo": 2,
"view": 0,
}
]
},
{
"name": "TapeTwo",
"elements": [
{
"propsOne": one,
"propsTwo": two,
"view": 0,
},
{
"propsOne": 1,
"propsTwo": 2,
"view": 0,
}
]
}
],
"typeOptions": []
}
Well, so far so good, I can form a dynamic generation of Tapes and Elements according to the user's request, but there is a moment when the user asks for an option selector and the value I get in Elements is in the View, where number by the value of 1 is required.
{
"tapes": [
{
"name": "",
"elements": [
{
"propsOne": "",
"propsTwo": "",
"view": 1,
}
]
}
],
"typeOptions": [
{
"values": []
}
]
}
It is at this point that I need to retrieve the values that it passes on in these fields that will be dynamically generated and insert them into a new array that is called typeOptions in its values, the flow being like this:
The user will ask for a field who has multiple fields, like a selector.
This field will be generated in Tapes, Elements with the value being assigned to view with 1
The user will pass the values in these fields and they will be placed in typeOptions.values
So, the user add this new field and when this happens, I need pass the values that will be passed on fields will be generated for him.
The result needs be like this:
{
"tapes": [
{
"name": "",
"elements": [
{
"propsOne": "One",
"propsTwo": "Two",
"view": 1,
}
]
}
],
"typeOptions": [
{
"values": ["ValueOne", "ValueTwo", "ValueThree"]
}
]
}
Or when two Elements has the same view like 1:
{
"tapes": [
{
"name": "",
"elements": [
{
"propsOne": "One",
"propsTwo": "Two",
"view": 1,
},
{
"propsOne": "1",
"propsTwo": "2",
"view": 1,
}
]
}
],
"typeOptions": [
{
"values": ["ValueOne", "ValueTwo", "ValueThree"]
},
{
"values": ["Value1", "Value2", "Value3"]
}
]
}
I've already tried to use two <FieldArray /> having one inside the other, as I already do for Tapes and Elements, because with this I could use the map function to get the view value and add a conditional to permeate two new <FieldArray /> to add the values not only for each object selected inside the typeOptions, but also for each value that is inserted together.
What is the best way and best practice to do this?

What is the purpose of "additionalItems" property on arrays in rjsf?

I'm studying rjsf documentation and I'm confused about the additionalItems section of array docs.
Here is the example code from the docs:
const Form = JSONSchemaForm.default;
const schema = {
type: "array",
items: {
type: "string"
},
additionalItems: {
type: "boolean"
}
};
ReactDOM.render((
<Form schema={schema} />
), document.getElementById("app"));
and here is the official codepen
The rendered form seems to behave exactly the same if I remove the additionalItems, so what is the purpose? I guess it has one, since it's explicitly brought up in the docs, but I can't figure it out :)
additionalItems: <schema> is used to indicate how items are treated beyond the enumerated items you specify in the schema, or even if they are allowed at all.
In this schema, the first array item must be a string, the second must be a number, but any additional items beyond that are allowed and can be anything:
{
"type": "array",
"items": [
{ "type": "string" },
{ "type": "number" }
]
}
In this schema, the first item must be a string, the second must be a number, and all items after that must be booleans:
{
"type": "array",
"items": [
{ "type": "string" },
{ "type": "number" }
],
"additionalItems": { "type": "boolean" }
}
And in this schema, there are no more items permitted at all after the first two:
{
"type": "array",
"items": [
{ "type": "string" },
{ "type": "number" }
],
"additionalItems": false
}
You can read more about this keyword in the documentation: https://json-schema.org/understanding-json-schema/reference/array.html

How to convert long string to list of objects

How can I convert the following into a list of objects with javascript.
data:
{"_id":{"$oid":"5d96f2d3db90fded9f3fa2d1"},"name":"Audi","price":52642},{"_id":{"$oid":"5d96f2dedb90fded9f3fa2d2"},"name":"Mercedes","price":57127},{"_id":{"$oid":"5d96f304db90fded9f3fa2d3"},"name":"Skoda","price":9000},{"_id":{"$oid":"5d96f368db90fded9f3fa2d4"},"name":"Volvo","price":29000},{"_id":{"$oid":"5d96f370db90fded9f3fa2d5"},"name":"Bentley","price":350000},{"_id":{"$oid":"5d96f378db90fded9f3fa2d6"},"name":"Citroen","price":21000},{"_id":{"$oid":"5d96f37fdb90fded9f3fa2d7"},"name":"Hummer","price":41400},{"_id":{"$oid":"5d96f389db90fded9f3fa2d8"},"name":"Volkswagen","price":21600}
When I do typeof on the data I get string. What I am trying to do is create a list with an entry for every object. I tried JSON.parse but it errors on the commas between the curly braces.Thank you.
For that particular string – comma-separated JSON objects – you can wrap it in array brackets [] and parse it:
var data = `{"_id":{"$oid":"5d96f2d3db90fded9f3fa2d1"},"name":"Audi","price":52642},{"_id":{"$oid":"5d96f2dedb90fded9f3fa2d2"},"name":"Mercedes","price":57127},{"_id":{"$oid":"5d96f304db90fded9f3fa2d3"},"name":"Skoda","price":9000},{"_id":{"$oid":"5d96f368db90fded9f3fa2d4"},"name":"Volvo","price":29000},{"_id":{"$oid":"5d96f370db90fded9f3fa2d5"},"name":"Bentley","price":350000},{"_id":{"$oid":"5d96f378db90fded9f3fa2d6"},"name":"Citroen","price":21000},{"_id":{"$oid":"5d96f37fdb90fded9f3fa2d7"},"name":"Hummer","price":41400},{"_id":{"$oid":"5d96f389db90fded9f3fa2d8"},"name":"Volkswagen","price":21600}`;
var parsed = JSON.parse(`[${data}]`);
Then parsed will be an array of 8 entries:
[
{
"_id": {
"$oid": "5d96f2d3db90fded9f3fa2d1"
},
"name": "Audi",
"price": 52642
},
{
"_id": {
"$oid": "5d96f2dedb90fded9f3fa2d2"
},
"name": "Mercedes",
"price": 57127
},
{
"_id": {
"$oid": "5d96f304db90fded9f3fa2d3"
},
"name": "Skoda",
"price": 9000
},
...

How can I merge and match 2 separate objects?

What I want
I have 2 different Java-Script arrays/objects but with matching Ids. I want to merge them into a new object. So both the main object data and any matched elements from the secondary object are merged into a combined result.
What I tried
I tried using Object.assign() function but with no success.
Given Input
Example code, so I have 2 separate objects (main and lines):
let main = [
{
"Id": "1",
"Name": "Testing data"
}
]
let lines = [
{
"OtherId": "1",
"code": "AU-29830"
},
{
"OtherId": "1",
"code": "AU-29854-Single"
},
{
"OtherId": "1",
"code": "TV-BB21084623"
},
{
"OtherId": "2",
"code": "Don't Merge"
},
{
"OtherId": "3",
"code": "Don't Merge"
}
]
Expected Output
I want to merge those 2 arrays, so that the output should be a single array containing the merged main-object. This merged main-object should contain the original content of itself plus nested the filtered secondary array (only containing matching objects). The filtering was done using the id from the main array's object which has to match (the slightly deviating id) from each object of the secondary-array.
The resulting array should look like this:
let result = [
{
"Id": "1",
"Name": "Testing data",
"lines": [
{
"OtherId": "1",
"ProductCode": "AU-29830"
},
{
"OtherId": "1",
"ProductCode": "AU-29854-Single"
},
{
"OtherId": "1",
"ProductCode": "TV-BB21084623"
}
]
}
]
As your main is an array, I'm assuming you might end up with more than one main item in it. If so, here's one way to merge your line items onto each one:
const mergedMainItems =
main.map(mainItem=>({
...mainItem,
lines: lines.filter(line=>mainItem["Id"] === line["OtherId"])
}))
I think for this example this will work:
let result = [];
result.push({...main[0]}); //or even result.push(main[0])
result[0].lines = [];
for(let l in lines){
if(lines[l].code != "Don't Merge"){
result[0].lines.push({OtherId: lines[l].OtherId, ProductCode: lines[l].code})
}
}

Nested forEach issue

I have two arrays of object, the first array (printers, around 80 elements) is made of the following type of objects:
[{
printerBrand: 'Mutoh',
printerModel: 'VJ 1204G',
headsBrand: 'Epson',
headType: '',
compatibilty: [
'EDX',
'DT8',
'DT8-Pro',
'ECH',
],
cartridges: [],
},
....
]
The second array (cardridges, around 500 elements) is made of the following type of objects:
[
{
"customData": {
"brand": {
"value": {
"type": "string",
"content": "ECH"
},
"key": "brand"
},
"printer": {
"value": {
"type": "string",
"content": "c4280"
},
"key": "printer"
}
},
"name": "DT8 XLXL",
"image": {
"id": "zLaDHrgbarhFSnXAK",
"url": "https://xxxxxxx.net/images/xxxxxx.jpg"
},
"brandId": "xxxxx",
"companyId": "xxxx",
"createdAt": "2018-03-26T14:39:47.326Z",
"updatedAt": "2018-04-09T14:31:38.169Z",
"points": 60,
"id": "dq2Zezwm4nHr8FhEN"
},
...
]
What I want to do first is to is to iterate through the first array and and then iterate for all the cardridge available: if a the value customData.brand.value of a cardridge is included inside the array 'compatibility' of a printer, then I have to add this cardridge object inside the cardridges array of this printer. I have tried but somehow the iteration doesn't take place correctly. This is what I tried:
printers.forEach((printerItem) => {
const printer = printerItem;
printer.compatibilty.forEach((compatibilityItem) => {
const compatibility = compatibilityItem;
cardridges.forEach((cartridge) => {
if (compatibility === cartridge.customData.brand.value.content) {
printer.cartridges.push(cartridge);
}
});
});
});
What am I doing wrong?
You're accessing the wrong property. It should be cartridge.customData.brandName.value.content, carefully note brandName.value rather than brand.value
Your issue is that you're accessing it by the wrong property - brand and not brandName.
Furthermore, if you're targeting everything but IE, you could simplify your nested for loops to utilize some fancy ES6 array methods.
printers.forEach((p) => {
p.cartridges.push(cartridges.filter((c) => {
const brandName = c.customData.brandName.value.content;
return p.compatibilty.includes(brandName);
}));
});

Categories