Compare a string with multiple JSON property values using JSONPath - javascript

I'm building a search suggestion text box control in JavaScript and am trying to find a way to compare the string the user typed against a JSON Object that represents the user's contact list.
The JSON Object looks like this:
var contacts = {
'gmail' : [
{ name : 'Joe Smith', email : 'joe.smith#gmail.com' },
{ name : 'James Simpson', email : 'jim.simpson#gmail.com' }
]
}
Using JSONPath, I've been able to successfully compare the string the user typed against a single field in the contact object (ie. I can test the name, or the email) using the following:
var input = "james";
var search_path = '$.*[?( /' + input + '/i.test(#.name))]';
var results = jsonPath(contacts ,search_path, {resultType:"VALUE"});
Which returns the {James Simpson} contact object, but if I had typed Jim instead of James it would return nothing unless I did two separate JSONPath queries - one against the name and the other against the email.
What I'm looking is an elegant way to do an OR operator with JSONPath so I can test a single string against multiple JSON property values.
Here's the psuedo-code (non-working) that describes what I'm looking for:
var search_path = '$.*[?( /' + input + '/i.test([ #.name, #.email ]))]';
Does anyone know of a way to do this?

I would create a simpler data structure that maps search terms to a contact name. Once you have a contact name, look up the entire record using jsonPath

A better way is to use DefiantJS (http://defiantjs.com). This lib extends the global object JSON with the method "search" - with which you can query JSON structure with XPath expressions. This method returns the matches in an array (empty if no matches were found).
Here is a working JSfiddle of the code below;
http://jsfiddle.net/hbi99/z2Erf/
var data = {
"gmail": [
{
"name": "Joe Smith",
"email": "joe.smith#gmail.com"
},
{
"name": "James Simpson",
"email": "jim.simpson#gmail.com"
}
]
},
res = JSON.search( data, '//gmail[contains(., "jim")]' );
console.log( res[0].name );
// James Simpson
The expression '//gmail[contains(., "jim")]' will find all fields under GMAIL regardless of field name. To explicitly constrict the search to the fields "name" and "email", then the query should look like this:
'//gmail[contains(name, "jim") or contains(email, "jim")]'
To get an idea of how powerful XPath is, check out this XPath Evaluator tool;
http://www.defiantjs.com/#xpath_evaluator

If you are using Goessner's parser, you can use the || operator in your expression as follows:
var search_path = '$.*[?(/jim/i.test(#.name) || /jim/i.test(#.email))]';

Related

How to do in Karate API a Select * from 'somewhere' WHERE email LIKE 'something'?

I want to retrieve an arraylist with all the ids of the users that have a specific email domain (exe: #generatedEmail.com)
This is an example of how the json would look like; basically a Json Array with Json objects.I need to get a list with the ids of the objects that contain #generatedEmail.com in the email field.
[{
"id": "1234-5678-7890-1231",
"email": "blabla#generatedEmail.com",
}, {
"id": "gsdg4-fc32-dsfs-4213",
"email": "another#generatedEmail.com",
},{
"id": "pgo4-ffx2-621s-gju3",
"email": "otheremail#dontdelete.com",
}]
My end purpose is to pass this list of ids as parameters to a DELETE endpoint. I found in the Karate documentation that if I pass the list as a parameter when I call the feature file where I describe the Delete steps, it will act as a for each and fire the request for each id in the list.
Thank you in advance!
I tried with different Js functions, but I suck at it and had no success. The below returns to me the emails, but I don't know how to get their related ids. I thought to do the same thing with the ids then match them based on index, but I feel that I would be overengineering it and there must be something simpler and smarter.
* def emails = karate.jsonPath(usersList.response,"$..email")
* def condition = function(x){return x.includes('generatedEmail.com')}
I also tried this with the belief that I would get an array of the objects that I want from which I can later extract only the ids in another arraylist:
* def ids = []
* def fun = function(i){if(i.includes('generatedEmail')) {ids.add(i)}}
* karate.repeat(usersList.response, fun)
Then I also tried this but to no avail
* eval karate.forEach(usersList.response, function(user){if(user.email.includes('generatedEmail')) { ids.add(user.id)} })
Please forgive my lack of knowledge and ignorance :D
Just for your reference, you can do this in one-line if you are familiar with JS. It is elegant and kind of fun:
* def response =
"""
[
{
id: '1234-5678-7890-1231',
email: 'blabla#generatedEmail.com'
},
{
id: 'gsdg4-fc32-dsfs-4213',
email: 'another#generatedEmail.com'
},
{
id: 'pgo4-ffx2-621s-gju3',
email: 'otheremail#dontdelete.com'
}
]
"""
* def filtered = response.filter(x => x.email.includes('#generatedEmail.com')).map(x => x.id)
* match filtered == ['1234-5678-7890-1231', 'gsdg4-fc32-dsfs-4213']
I figured out the answer to my question.
The function filters and returns all json objects that contain the 'generatedEmail.com' pattern in their email fields.
Then I use the returned list and deepscan it to only retrieve the id fields in another list.
* def condition = function(x){return x.email.includes('generatedEmail.com')}
* def filtered = karate.filter(response, condition)
* def ids = karate.jsonPath(filtered,"$..id")

Dynamic keys in JSON document using Node.js

This question is specific to Node.js ES6.
I'm trying to create a JSON document to insert into DB (Mongo) the keys for inserting document would be values from input. Example
My input looks like this
[
"key1":"val1",
"key2":"val2",
"key3":"val3",
"key4":"val4"
]
My document that will be generated based on the values sent, so the document being inserted should look like
{
"val1":"someOtherVal1",
"val2":"someOtherVal2",
"val3":"someOtherVal3",
"val4":"someOtherVal4"
}
const input = {var1: "v1", var2: "v2"};
const mongoObj = {
[input.var1]: "someOtherVal1",
[input.var2]: "someOtherVal2"
};
console.log(mongoObj);
You want to use computed property put your variable between brackets:
{
[variable] : "value"
}

How do I create user defined types (ala C#) to use in objects in JavaScript?

In C#, I can create a class that acts as a user-defined type, such as:
Public Class FullName
{
string FirstName;
string LastName;
}
Public Class Address
{
string Line1;
string Line2;
string City;
string State;
string Zip;
}
and then I can create:
Public Class Person
{
FullName Name;
Address HomeAddress;
Address WorkAddress;
}
This allows me to reference the data like:
Person Bob;
Bob.WorkAddress.Line1 = "123 Sycamore Rd";
Bob.HomeAddress.Line1 = "1313 MockingBird Ln";
Bob.FullName.LastName = "Smith";
etc...
Ultimately, I want to create a 2D array of Person, so I don't want to hardcode (pre-populate?) the data until I know what it is.
I'd like to be able to do the same thing in JavaScript (specifically node.js), but can't seem to find an obvious way of doing so. Is this just fundamentally the wrong approach, or am I just missing something?
In Javascript you can create data objects directly (no classes):
var bob = {
workAddress: { line1: "123 Sycamore" },
fullName: { lastName: "Smith" }
};
It's also possible to create a class, but it's usually not necessary for mere data objects.
Ultimately, I want to create a 2D array of Person, so I don't want to hardcode (pre-populate?) the data until I know what it is.
You can create an array and later add persons to it:
var persons = [];
...
persons.push(bob);
For a 2D array, create an array to contain your person arrays:
var persons2D = [];
...
persons2D.push(persons);
A really good example of javascript objects and their notation would be JSON.org
Here is an object that has 2 string properties, and a 3rd property which is an array. One slight difference is that because javascript is not strongly typed, the "residents" property could just be a simple string or an array. So you have to be careful when parsing as any property could be either a string or another array.
var household = {
address: "1234 N 56th st"
, city: "somewhere"
, residents: [
{ Name: "My Name" }
, { Name: "My Alias" }
]
};
Now depending on how you are sourcing the data, you can use the javascript or JSON (de)serialize methods within C# to populate.

Regular Expressions on array

I am trying to edit the first entry in a array before it placed in another file.
This is it:
(["\"NAMES\":\"cs.js\"},[
I want to turn it into this:
([{"NAMES":"cs.js"},[
I'm using an online regex generator, but so far I've only managed to edit to this point with /.["[\]/ and substituting with ([{:
([{"NAMES\":\"cs.js\"},[
Any help given will be appreciated.
EDIT:
Here is some of the code:
var initialCourseArray = new Array()
initialCourseArray.push(["\"NAMES\":\"cs.js\"},[
{"COURSE_ID":"ENGL 1013"},
{"COURSE_ID":"FAH1"},
{"COURSE_ID":"USHG1"},
{"COURSE_ID":"TECH 1001"},
{"COURSE_ID":"COMS 1403"},
{"COURSE_ID":"COMS 1411"},
{"COURSE_ID":"ENGL 1023"},
{"COURSE_ID":"SS1"},
{"COURSE_ID":"MATH 2914"},
The stuff after is the rest of the values in the array and they do not look like this one so I'm not worried about them.
Second EDIT:
Since there is some confusion about the code that I honestly should have placed in here first, I am using a php file to retreive course data from a test database and then encoding it into JSON, formatting it, and then using fopen and fprintf to place it inside a javascript file. The part I'm giving you is what ends up inside the javascript file.
Third EDIT:
here is the code I am using to format the array. It is very messy because my leader keeps changing the format he wants the result to be in:
$row1 = "\"NAMES\"";
$colon = ":";
$row2 = "\"".$major.".js\"";
$major_name = $row1.$colon.$row2;
//The course data is already loaded into the table. This why I am using array_unshift to place the major_name inside.
array_unshift($major_array, $major_name);
array_push($major_array, "false");
$json_string = json_encode($major_array);
$re = "/.,/";
$subst = "},\r\n";
$json_string = preg_replace($re, $subst, $json_string);
$re2 = "/\,(?=[^.]*$)/";
$subst2 = ",[";
$json_string = preg_replace($re2, $subst2, $json_string, 1);
$first_string = "var initialCourseArray = new Array()";
$second_string = "initialCourseArray.push(";
$end_bracket = "]";
$end_parentheses =")";
There are several issues:
1. Don't manipulate JSON strings
You should never manipulate a string that is the result of json_encode, because you will very likely make the JSON text invalid, which is actually happening in your case.
So using this kind of statements:
$json_string = preg_replace($re, $subst, $json_string);
is asking for trouble. Once you have a $json_string, it should be final. Anything you want to happen to the structure must happen before you call json_encode.
Even if you just want to add line breaks inside a JSON string, don't do it that way. json_code provides a "pretty print" option which will do it for you:
json_encode(...., JSON_PRETTY_PRINT);
2. JavaScript does not have associative arrays
A second problem is that in JavaScript you cannot have something like
["NAMES":"cs.js" ...]
So json_encode will never generate anything like that. If you want named keys in JavaScript (like "NAMES"), you cannot define it as an array, but should define it as an object:
{"NAMES":"cs.js" ...}
json_encode will do that for you if you provide it the corresponding PHP structure (i.e. an associative array) and let it do its job without tampering.
3. Don't add "false"
It does not seem useful to add "false" as an element to the courses array. In JavaScript you can easily check how many elements there are in an array, so there is no need to put a kind of stop-sign at the end.
Anyway, if in JavaScript you refer to an element in an array that does not exist, you get undefined, which you can verify, much like verifying for the value "false".
I would strongly suggest to leave that out.
Suggested code
The PHP code you provided in your question could be replaced with this:
// Add the names element as a separate item next to the courses array,
// which we put in the "courses" property.
$major_array = array(
"names" => $major,
"courses" => $major_array
);
// Turn into JSON text with added line breaks and indentation:
$json_string = json_encode($major_array, JSON_PRETTY_PRINT);
// Don't touch the JSON text anymore, but output it:
echo "var initialCourse = $json_string;";
The output (JavaScript) would be something like:
var initialCourse = {
"names": "cs",
"courses": [
{
"COURSE_ID": "ENGL 1013"
},
{
"COURSE_ID": "FAH1"
},
{
"COURSE_ID": "USHG1"
},
{
"COURSE_ID": "TECH 1001"
},
{
"COURSE_ID": "COMS 1403"
},
{
"COURSE_ID": "COMS 1411"
},
{
"COURSE_ID": "ENGL 1023"
},
{
"COURSE_ID": "SS1"
},
{
"COURSE_ID": "MATH 2914"
}
]
};
As I mentioned above, this is an object structure, not an array structure, because JavaScript does not allow named keys in an array notation. If in JavaScript you need to iterate over the courses in the above structure, you would address the courses property (which is an array), like this:
for (var course of initialCourse.courses) {
console.log('course id: ' + course.COURSE_ID);
}
More concise structure
I must say it is a bit of an over-kill to have objects with just one property. This structure would be more concise and efficient:
var initialCourse = {
"names": "cs",
"courses": [
"ENGL 1013",
"FAH1",
"USHG1",
"TECH 1001",
"COMS 1403",
"COMS 1411",
"ENGL 1023",
"SS1",
"MATH 2914"
]
};
In JavaScript you would iterate over these courses like this:
for (var course of initialCourse.courses) {
console.log('course id: ' + course);
}
If this interests you, you should just add this line to your PHP code, before any of the PHP code I suggested above:
$major_array = array_map(function ($course) { return $course["COURSE_ID"]; }, $major_array);
If you just want to apply it to that line,
find /"?\\"/ and replace " will do it.

Searching json array for a specific attribute

Actually I want to search an attribute's value in an json array for one of its child. Now one condition is that the attribute will not be there in all the child's of the array. This is my json array.
[{
"heading1":"heading1",
"heading2":"heading2",
"heading3":"heading3",
"heading4":"heading4",
"heading5":"heading5",
"heading6":"heading6"
},
{
"column1":65536,
"column2":"school",
"column3":"testing purpose",
"column4":"DESKTOP",
"column5":"ACTIVE",
"column6":true,
"column7":"a6cc82e0-a8d8-49b8-af62-cf8ca042c8bb"
},
{
"column1":98305,
"column2":"Nikhil",
"column3":"Test",
"column4":"LAPTOP",
"column5":"ACTIVE",
"column6":true,
"column7":"a6cc82e0-a8d8-49b8-af62-cf8ca042c8bb"
}]
So presently I am working with the each loop but like this
var obj = $.parseJSON(JSON.stringify(response));
$.each(obj, function () {
console.log("heading1", this['heading1']);
});
Here response comes from mserver and it is the json array
Now I want to know can I search for this attribute in the json array without using a loop in jQuery.
Based on your sample code what I understand you have is an array of objects and you want to find objects with one specific property and or value:
This will return true if the object has the property
var results= arr.filter(function(item){ return item.hasOwnProperty("column5"); });
Or you can perform additional action when you find the property:
arr.filter(function(item){
if (item.hasOwnProperty("column5")) {
return item["column5"] === 'demo 01'; //or item.column5 === 'demo 01'
}
return false;
});
This only works on IE9+ if you need this to run in older versions of IE, please follow the instructions under polyfill:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
The you can check like
var obj = $.parseJSON(response);
$.each(obj, function (index,value) {
if(typeof obj[index].heading2 !== "undefined")
{
alert(obj[index].heading2);
}
when in other object of array element not find then it returns undefined. and you can check like that.
you can check in this http://jsfiddle.net/gKRCH/
It's best to use a loop. But if the format of the JSON is regular, you could regex for the value in the response string.
I'm not recommending this method, just pointing out that it exists.
var value = "heading1";
if( (new RegExp('"' + value + '"')).test(response) ){
// Found value
};
Here, we take the required value, wrap it in quotation marks and search for it in the response.
This has several issues, such as:
It might find the pattern in a property name
If the value could contain regex special characters, they'll need escaping.
If your JSON contains values with escaped quotation marks, you could get a false positive from partial matches.
That's why it depends on you knowing the format of the data.
EDIT:
You can solve issue 2 by using this condition instead of regex. But it gives you less flexibility.
response.indexOf('"' + value + '"') !== -1
Try this,
$.each(object,function(key, value){
console.log(key);
console.log(value);
});
You can use this JS lib; DefiantJS (http://defiantjs.com). This lib extends the global object JSON with the method "search" - with which, you can perform XPath queries on JSON structures. Like the one you have exemplified.
With XPath expressions (which is standardised query language), you can find whatever you're looking for and DefiantJS will do the heavy-lifting for you - allowing your code to be neat and clean.
Here is the fiddle of this code:
http://jsfiddle.net/hbi99/q8xst/
Here is the code:
var data = [
{
"heading1": "heading1",
"heading2": "heading2",
"heading3": "heading3",
"heading4": "heading4",
"heading5": "heading5",
"heading6": "heading6"
},
{
"column1": 65536,
"column2": "school",
"column3": "testing purpose",
"column4": "DESKTOP",
"column5": "ACTIVE",
"column6": true,
"column7": "a6cc82e0-a8d8-49b8-af62-cf8ca042c8bb"
},
{
"column1": 98305,
"column2": "Nikhil",
"column3": "Test",
"column4": "LAPTOP",
"column5": "ACTIVE",
"column6": true,
"column7": "a6cc82e0-a8d8-49b8-af62-cf8ca042c8bb"
}
],
res = JSON.search( data, '//*[column4="DESKTOP"]' );
console.log( res[0].column2 );
// school

Categories