I make json validation with ajv. I need to validate array of strings. I know which elements can be placed there so I make appropriate 'enum'. But in some case enum can be empty and array can be empty too. Here is simple test:
var schema = {
"type":"array",
"items" : {
"type" : "string",
"enum" : []
}
}
var data = [];
var Ajv = require('./ajv-4.1.1.js');
var ajv = Ajv({
allErrors : true
});
var validate = ajv.compile(schema);
var valid = validate(data);
if (!valid)
console.log(validate.errors);
As a result I get:
Error: schema is invalid:data.items.enum should NOT have less than 1 items, data.items should be array, data.items should match some schema in anyOf
I can add any fictive string to enum array but is it possible to validate this case in legal way? Adding 'minItems=0' restriction doesn't help.
Is it really json schema draft restriction that I can't use empty enum?
UPD: I expect to validate code in general case:
var array = Object.keys(someObj); // array: ["foo", "bar"]
var schema = {
"type":"array",
"items" : {
"type" : "string",
"enum" : array
}
}
var data = ["foo"]; // valid
var data = ["bar"]; // valid
var data = ["bar","foo"]; // valid
I expect to validate code in special case:
var array = Object.keys(someObj); // array: []
var schema = {
"type":"array",
"items" : {
"type" : "string",
"enum" : array
}
}
var data = []; // I expect to see it valid too but get error instead.
The enum keyword is required to have at least one value. The specification states ...
5.5.1.1. Valid values
The value of this keyword MUST be an array. This array MUST have at least one element. Elements in the array MUST be unique.
Elements in the array MAY be of any type, including null.
http://json-schema.org/latest/json-schema-validation.html#anchor76
This makes sense because an empty enum would mean nothing would ever validate. However, I do see how it could come in handy in your particular case. If you need to build the schema dynamically, you will need to check for the empty array case and use a different schema.
Here's one way to do it:
{
"type": "array",
"maxItems": 0
}
Here's another:
{
"type": "array",
"not": {}
}
Related
In my MongoDB/Node backend I am taking in passed-in parameters and then filtering on some data.
The first thing I do is take in string values (that happen to represent numbers) such as "1", "2", etc. and turn them into a comma-separated array.
Now I need to filter on a property called "responsibilitySequence". The thing is, the values for this property are of type "number", not "string".
So how do I adjust the following function so that what gets passed-in as an array that Mongo will evaluate using the $in operator, consists of numbers, not string values?
if (responsibilitySequence) {
let arrResponsibilitySequence = [];
arrResponsibilitySequence = responsibilitySequence.split(",");
_.each(arrResponsibilitySequence, (l, key, c) => {
arrResponsibilitySequence[key] = new RegExp(arrResponsibilitySequence[key], "i");
});
search.responsibilitySequence = {
$in: arrResponsibilitySequence
};
}
You could just map the values and parse them as int:
if (responsibilitySequence) {
let arrResponsibilitySequence = responsibilitySequence.split(",");
search.responsibilitySequence = {
$in: arrResponsibilitySequence.map(x => Number.parseInt(x)) // or just +x
};
}
I've two arrays:
var filteredTitles = [String]()
var filteredTypes = [String]()
I filter the first array as a part of using searchbar. The order of the elements might change completely. However, I can't filter the second array the same way I did the first one, because I don't want to take it in to count when searching. But I would like for the second array to be in the same order as the first one. So, to recap. How can I filter an array to match another one by indexes perfectly?
An example:
var filteredArray = ["One", "Two", "Three"]
//Sort the below array to ["1", "2", "3"], the order of the upper array
var toBeFilteredArray = ["2", "1", "3"]
WITHOUT using alphabetical or numerical order, as that won't do in this case.
EDIT:
TO Russell:
How do I sort the titles like this:
// When there is no text, filteredData is the same as the original data
// When user has entered text into the search box
// Use the filter method to iterate over all items in the data array
// For each item, return true if the item should be included and false if the
// item should NOT be included
searchActive = true
filteredData = searchText.isEmpty ? original : original.filter({(dataString: String) -> Bool in
// If dataItem matches the searchText, return true to include it
return dataString.range(of: searchText, options: .caseInsensitive) != nil
})
don't have two arrays - have a single array of a custom type, containing both variables that you need
Define your struct
struct MyCustomData
{
var dataTitle : String = ""
var dataType : String = ""
}
and then declare it
var dataArray : [MyCustomData] = []
populate it and sort it where required - I have populated in reverse order just so that we can see it being sorted
dataArray.append(MyCustomData(dataTitle: "Third", dataType: "3"))
dataArray.append(MyCustomData(dataTitle: "Second", dataType: "2"))
dataArray.append(MyCustomData(dataTitle: "First", dataType: "1"))
let filteredArray = dataArray.sorted {$0.dataTitle < $1.dataTitle}
for filteredElement in filteredArray
{
print("\(filteredElement.dataTitle), \(filteredElement.dataType)")
}
// or, to print a specific entry
print("\(filteredArray[0].dataTitle), \(filteredArray[0].dataType)")
An example of keeping two separate arrays in sync using zip:
let titles = ["title1", "title3", "title4", "title2"]
let types = ["typeA", "typeB", "typeC", "typeD"]
let zipped = zip(titles, types)
// prints [("title4", "typeC"), ("title2", "typeD")]
print(zipped.filter { Int(String($0.0.characters.last!))! % 2 == 0 })
You can use map on the filtered result to get back two separate filtered arrays for the titles and types.
Here I have this json file.
{
"BnUs5hQZkJWLU9jGlpx9Ifq5ocf2" : {
"bio" : "Your bio!\r",
"birthday" : "Date of Birth?",
"location" : "Location?",
"markerBorder" : 1.5542403222038021E7,
"markerColor" : 8222122.31461079,
"name" : "NamesName?",
"profilePrivacy" : 2,
"sex" : "Gender?",
"privacy" : 2,
"points" : {
"-Kc7lfJk3XbPlNyk-wIR" : {
"address" : "dsfsdfasfsfd",
"description" : "status/desription",
"latitude" : 35.2,
"longitude" : -80.7,
"mediaTargets" : "none",
"pub" : false,
"timestamp" : 1486205926658
},
"aaa" : "aaa"
}
}
}
Those random string of charactors are automatically made when I use firebase.
In this scenario, there might be more "points" I will have to take account for. So when I reference points, I should be talking to an array since it contains both "-Kc7lfJk3XbPlNyk-wIR" (an array) and "aaa" (a string).
So why do I get a type error when trying to convert parsedObject.points into an array?
var parsedObject:Object = JSON.parse(e.target.data);
var multiArray:Array = parsedObject.points;
TypeError: Error #1034: Type Coercion failed: cannot convert Object#5c16089 to Array.
I'm basically trying to do the opposite of what this guy is doing.
Edit: I see in the notes that it only handles string, numbers and Boolean values..
I managed to work around it by adding a "parent" node in the object that duplicates the same value as the name of the entire node so I can reference it in the script. Is there a better way to go about this? Seems pretty redundant.
var parsedObject:Object = JSON.parse(e.target.data);
var myPoints:Object = parsedObject["points"];
//Get all trek names
for each (var key:Object in myPoints)
{
trace("Key = " + key.parent);
trace(parsedObject.treks[key.parent].latitude) //Returns -80.7
}
Because Array is a subclass of Object.
var A:Array = new Array();
var B:Object = new Object();
trace(A is Array); // true
trace(A is Object); // true
trace(B is Array); // false
trace(B is Object); // true
B = new Array(); // nothing wrong here
A = new Object(); // throws exception
So, you might want to tell what kind of data you want to obtain in the Array form from the parsedObject.points Object to proceed.
Alternately, that is how you get actual Array from JSON string:
{
"list": [1,2,3,4,5,6]
}
Looks like it's correctly being parsed by JSON.parse to me.
Arrays in JSON use square brackets, braces are interpreted as objects.
You'd only expect an Array from JSON.parse if you had
"points": [
...
]
whereas this is an Object:
"points": {
...
}
I suggest you look into why you aren't getting [] from your source.
Is there any way to read an object from a list of objects dynamically with object key.
My complex objects is something like :
$scope.mainObject = {
name : "Some value",
desc : "Some desc",
key1: {
arrayLst: []
},
key2: {
arrayLst: []
}
}
In my method, I have the key value either key1 or key2 in a string keyValue. How can I write to object like :
$scope.mainObject.key1.arrayLst = data;
In the form of something like :
$scope.mainObject.get(keyValue).arrayLst = data;
Well, there's something known as Array notation in JavaScript objects. More on it here.
You can write it something like this :
$scope.mainObject.[keyValue].arrayLst = data;
I have a array, which has the bunch of object, i would like to filter the object by 'name' value, again i would like to omit those object from another array of object using underscore.
I know that we can do using earch, but i am not getting the proper approach to do this both..
any one help me to do this?
example :
incoming array:
var incomingArray = [
{"name":"apple"},
{"name":"orange"},
{"name":"dog"},
{"name":"cat"},
{"name":"egle"}
];
filter keys:
var omit = ['orange' ,'dog'];
//i need to check whether 'orange' or 'dog' are exist, if so..
var filtered = _.filter(incomingArray, function(obj, i){
return obj.name === omit[i]['name'];//this is wrong i need to loop again how?
});
var anotherArray = [
{"name":"apple"},
{"name":"orange"},
{"name":"dog"},
{"name":"cat"},
{"name":"egle"}
]
return only the array without the omit like this:
var outgoingArray = [
{"name":"apple"},
{"name":"cat"},
{"name":"egle"} ]
how we could achieve this with proper approach?
demo
You were nearly there! Use indexOf to check that the name does not belong in the omit array:
var filtered = _.filter(incomingArray, function(obj) {
return omit.indexOf(obj.name) == -1;
});