I have a set of JSON data that has multiple levels of data. Snippet of the JSON (ignore the missing formatting please):
DATA SET A:
"interaction": {
"author": {
"username": "johndoe",
"name": "John Doe"
}
},
"gender": "male"
DATA SET B:
"interaction": {
"author": {
"name": "Jane Doe"
}
},
"gender": "male"
In a single-level I can use:
if record.has_key?('gender')
and that will return a true/false value if the key is present.
If I try to seed data and the key isn't present, it will throw an error and stop seeding.
My question: How would I check to see if the "username" key exists. Data Set B, for example, doesn't have a username key and would throw an error, but I can't figure out how to modify the has_key() command to check for a few levels down.
Thanks for the help.
I've decided to work around the has_key method and used a begin/rescue/end.
begin
#data.username = record['interaction']['author']['username']
rescue
#data.username = nil
end
Here's a possibility:
# where a and b are loaded from JSON
a = {
'interaction' => {
'author' => {
'username' => 'johndoe',
'name' => 'jd'
}
}
}
b = {
'interaction' => {
'author' => {
'oijoijoij' => 'johndoe',
'name' => 'jd'
}
}
}
class Hash
def recursive_has_key?(key)
has_key?(key) or values.any? { |v| v.is_a?(Hash) and v.recursive_has_key?(key) }
end
end
puts a.recursive_has_key?('username')
puts b.recursive_has_key?('username')
Which outputs
$ ruby foo.rb
true
false
Related
In my Karate tests (0.9.4), I have a json response like the following:
[
{
"id": "id_number_1",
"name": "name"
},
{
"id": "id_number_2",
"name": "name 2",
"nestedThing" {
"id": "another_id",
"name": object2_name"
}
},
{
"id": "id_number_3",
"name": "name 3"
}
]
Some of the objects in the response will have a nestedThing and others will not. First, I want to get rid of all the items in the list that do not have nestedThing. Second, once that's done, I want to def a list that only contains the first-level id fields. So, it would look like:
["id_number_1", "id_number_3"]
This can be done in one line:
* def ids = response.filter(x => !x.nestedThing).map(x => x.id)
Refer: https://github.com/karatelabs/karate#json-transforms
EDIT: the below works in versions 0.9.X
* def temp = karate.filter(response, function(x){ return !x.nestedThing })
* def ids = karate.map(temp, function(x){ return x.id })
* match ids == ['id_number_1', 'id_number_3']
I have a JSON file as given below:
{
"lastname": {
"displayName": "lastname"
},
"#(dynamicKey)": {
"displayName": "#(dynamicKey)"
}
}
When I try to read the file, the key and values are not getting updated but when I use JSON like below, value got replaced by the def values. If I give dynamic key both key and value are not getting updated. :-(
{
"lastname": {
"displayName": "lastname"
},
"someKey": {
"displayName": "#(dynamicKey)"
}
}
Could you please help me on how to replace dynamic key and value?
This is how you can handle dynamic keys. The #(dynamicKey) embedded-expression approach will not work.
* def dynamicKey = 'bar'
* def test = { someKey: 'foo' }
* test[dynamicKey] = 'baz'
* match test == { someKey: 'foo', bar: 'baz' }
I am currently trying to remove a joint table data added when retrieving an association data.
The query is done by sequelize using a method added to the model through specifying model relationships(sequelize magic methods), for some reason, I'm not able to do that.
I have currently tried passing in attributes: {exclude: ['...']} to the method but the field still persists.
Current association
// Class sequelize model
Class.belongsToMany(models.Subject, {
through: 'ClassSubject',
foreignKey: 'class_id',
otherKey: 'subject_id',
as: 'subjects'
})
// Subject sequelize model
Subject.belongsToMany(models.Class, {
through: 'ClassSubject',
foreignKey: 'subject_id',
otherKey: 'class_id',
as: 'classes'
});
Query and Response
const subjects = await dbClass.getSubjects(); // dbClass is a Class model instance
// Response
[
{
"id": "1b89d44c-2caa-452d-a1f8-7faa11970917",
"name": "Mathematics",
"code": "MATHS",
"summary": "Mathematics for class 1",
"ClassSubject": {
"class_id": "637afc7b-40f6-478e-b35e-859ca462e2e7",
"subject_id": "1b89d44c-2caa-452d-a1f8-7faa11970917"
}
}
]
Desired output
// Response
[
{
"id": "1b89d44c-2caa-452d-a1f8-7faa11970917",
"name": "Mathematics",
"code": "MATHS",
"summary": "Mathematics for class 1"
}
]
I have tried passing options to the method as specified below but to no avail
const subjects = await dbClass.getSubjects({
attributes: { exclude: ['ClassSubject'] }
});
But it still doesn't work.
Try using the joinTableAttributes option and pass empty array to exclude everything in joint table.
const subjects = await dbClass.getSubjects({ joinTableAttributes: [] });
Create two indices in elasticsearch parent and child
PUT parent/car/sedan
{
"type": "sedan",
"details": {
"wheels": 4,
"doors": 4,
"seats": 5,
"fuel": "gasoline"
}
}
PUT child/toyota/corolla
{
"color": "white",
"type": "sedan",
"details": {
"wheels": 4,
"doors": 4,
"seats": 5,
"fuel": "gasoline"
}
}
SQL UPDATE by JOIN (the corresponding SQL version that we'll perform on elasticsearch using logstash)
update CHILD.doors = PARENT.doors
from PARENT, CHILD
where PARENT.type = CHILD.type
ELASTICSEARCH UPDATE by JOIN (execute logstash with the logstash.conf as mentioned below)
input {
elasticsearch {
docinfo => true
hosts => ["127.0.0.1:9200"]
user => "admin"
password => "pass"
index => "child"
query => '{ "query": { "match": { "type": "sedan" } } }'
}
}
filter {
mutate {
remove_field => ["message","#version","#timestamp"]
}
elasticsearch {
hosts => ["127.0.0.1:9200"]
user => "admin"
password => "pass"
index => "parent"
query => "type:sedan"
fields => { "details.doors" => "parent_doors"
"details.seats" => "parent_seats"
"type" => "parent_type"
}
}
prune {
whitelist_names => ["color","type", "details","parent_doors","parent_seats","parent_type"]
}
}
output {
stdout {
codec => rubydebug
}
elasticsearch {
hosts => ["127.0.0.1:9200"]
user => "admin"
password => "pass"
index => "%{[#metadata][_index]}"
document_type => "%{[#metadata][_type]}"
document_id => "%{[#metadata][_id]}"
action => "update"
doc_as_upsert => true
script_lang => "painless"
script => "if ( ctx._source.type == '%{parent_type}' ) { ctx._source.details.doors = %{parent_doors} }"
}
}
This Works. If you have a better way of achieving the same, please do let us know.
Data:
{
"contextTag": {
"value": "Bittersweet",
"valueLabel": "Bittersweet"
},
"tags": [
{
"name": "tag",
"value": "Creamy"
},
{
"name": "tag",
"value": "Colorful"
},
{
"name": "tag",
"value": "Bright"
}
],
"rating": 5,
"userNickName": "HelloGames",
"userLocation": "UK",
"title": "Great!",
"reviewText": "Yada yada yada yada",
"submissionTime": "30 Nov 16"
},
I currently have this working for getting contextTag valueLabels:
this.props.reviewData.reviews.map(
(o) => {
return o.contextTag && o.contextTag.valueLabel ? o.contextTag.valueLabel.trim() : '';
}
)
And this for tags:
this.props.reviewData.reviews.map(
(o) => {
return o.tags && o.tags.value ? o.tags.value.trim() : '';
}
)
But it's coming back empty. How do I loop through tags to grab each of the values?
You can cache the tags, then map over it to get the values. Like below:
const tags = this.props.reviewData.reviews.tags;
const tags_values = ( tags ? tags.map((tag) => (tag.value ? tag.value : '' ) : []); // this an array of the tags values.
Your code does not return what you want because the tags attributes is an array of objects, so to get the tag values you have to map over it as I did above.
Hope this helped.