XPath separate key and value of group - html

I'm trying to fetch data from a dd group that is not really well structured. The 'group' does have a DD wrapper but inside it's only p/div without a grouped wrapper around it:
[DD]
[P] Key
[DIV]
[P] Value
[P] Key
[DIV]
[P] Value
Is it possible to collect the data the proper way?
The html code I'm processing:
<dd class="product-specifications-v2__items">
<p class="product-specifications-v2__key">
EAN/UPC - product
</p>
<div class="product-specifications-v2__value">
<p class="product-specifications-v2__value-item">
7912372
</p>
</div>
<p class="product-specifications-v2__key">
Weight
</p>
<div class="product-specifications-v2__value">
<p class="product-specifications-v2__value-item">
2,170
<span>kg</span>
</p>
</div>
</dd>
I currently get the following result as a array:
{
"key": [
"EAN\/UPC - product",
"Weight"
],
"value": [
"7912372",
"2,170 kg",
]
}
And I need to get (without arrays):
{
"key": "EAN/UPC - product",
"value": "7912372"
},
{
"key": "Weight",
"value": "2,170 kg"
}
I'm fetching the data via an API with the following request:
{
"name":"attributes",
"selector":"div.product-specifications-v2__wrapper dl dd",
"targets":[
{
"name":"key",
"selector":"p.product-specifications-v2__key",
"dataType":"title"
},
{
"name":"value",
"selector":"div.product-specifications-v2__value p.product-specifications-v2__value-item",
"dataType":"title"
}
]
}

Using XPath 3.1 (for instance, inside the browser with Saxon-JS (https://www.saxonica.com/saxon-js/documentation2/index.html), also with Node) you can use a path expression that creates an XPath 3.1 XDM map with the key and value:
//dd[#class = 'product-specifications-v2__items']/p[#class = 'product-specifications-v2__key']!map { 'key' : normalize-space(), 'value' : following-sibling::div[#class = 'product-specifications-v2__value'][1]!normalize-space() }
const html = `<dd class="product-specifications-v2__items">
<p class="product-specifications-v2__key">
EAN/UPC - product
</p>
<div class="product-specifications-v2__value">
<p class="product-specifications-v2__value-item">
7912372
</p>
</div>
<p class="product-specifications-v2__key">
Weight
</p>
<div class="product-specifications-v2__value">
<p class="product-specifications-v2__value-item">
2,170
<span>kg</span>
</p>
</div>
</dd>`;
var htmlDoc = new DOMParser().parseFromString(html, 'text/html');
const results = SaxonJS.XPath.evaluate(`//dd[#class = 'product-specifications-v2__items']/p[#class = 'product-specifications-v2__key']!map { 'key' : normalize-space(), 'value' : following-sibling::div[#class = 'product-specifications-v2__value'][1]!normalize-space() }`, htmlDoc, { 'xpathDefaultNamespace' : 'http://www.w3.org/1999/xhtml' });
console.log(results);
<script src="https://www.saxonica.com/saxon-js/documentation2/SaxonJS/SaxonJS2.rt.js"></script>
The JavaScript API of Saxon-JS returns the sequence of XDM maps as an array of JSON objects to JavaScript.

Related

Change inner html dynamically from json

I have a json as follows -
{
"albums": {
"abc": "ABC",
"pqr": "PQR",
"lmn": "LMN",
"xyz": "XYZ"
}
}
In the html structure below, I want that each value in the json should be added in each subsequent p tag (in the same order)
<div>
<p><p>
<div>
<div>
<p><p>
<div>
<div>
<p><p>
<div>
<div>
<p><p>
<div>
(The divs are NOT be appended dynamically). I also don't want to pick each p tag and set value of each key to it one by one. I want this to happen automatically when I call the json ( which is stored in a separate file)
I am trying -
$(document).ready(function(){
$.getJSON("albums.json",
function (response) {
$.each(response.albums, function (key,value) {
$('p').each(function () {
$(this).html(value);
});
})
})
Doesn't give any output. Can anyone please tell me how to achieve this?
Thanks!!!
You can do it by getting the index of the item from your values and using that to target the right <p>:
var response = {
"albums": {
"abc": "ABC",
"pqr": "PQR",
"lmn": "LMN",
"xyz": "XYZ"
}
}
var $p = $('p');
Object.values(response.albums).forEach( (value,i) => {
$($p[i]).html(value);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
<p></p>
</div>
<div>
<p></p>
</div>
<div>
<p></p>
</div>
<div>
<p></p>
</div>
Your HTML isn't valid (none of <p> or <div> are closed).
let json = {
"albums": {
"abc": "ABC",
"pqr": "PQR",
"lmn": "LMN",
"xyz": "XYZ"
}
};
let i = 0;
$.each(json.albums, function (key,value) {
$(`p:eq(${i})`).html(value);
i++;
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
<p></p>
</div>
<div>
<p></p>
</div>
<div>
<p></p>
</div>
<div>
<p></p>
</div>

Separate and display multiple json value in different span tag in reactjs

I have a json object called blogData with json data. Inside the json obj the tags key may have multiple tag values. I would like to display the tags values separately in span tag while iterating using map function.
Now multiple tags are displaying in a single span tag ( please see below) How can I fix this ?
const blogData = [
{
"id" : 1,
"title":"Cypress tests",
"images":"/images/image1.jpg",
"description": "Add the E2E cypress UI tests",
"tags": "cypress"
},
{
"id" : 2,
"title":"Jmeter tests",
"images":"/images/image2.jpg",
"description": "Performance test using Jmeter tool",
"tags": ["jmeter", "performance"]
},
{
"id" : 3,
"title":"Basic Unix commands",
"images":"/images/image3.jpg",
"description": "Learn basic unix commands in git bash",
"tags": "unix"
},
{
"id" : 4,
"title":"Postman",
"images":"/images/image4.jpg",
"description": "Api testing using postman",
"tags": ["postman", "api"]
},
]
Home.js
const [searchResults, setSearchResults] = useState([]);
<div className="container">
{
searchResults.map(({ id, title, images, description, tags }) => (
<div key={id} className="column-center">
{/* <img className="blogImage" key={images} src={images} alt="image"></img> */}
<div className="blogtitle">
<span key={title}>{title}</span>
</div>
<section>
<p className="blogdescription" key={description}>{description}</p>
</section>
<section className="col1">
<span key={tags}>{tags}</span>
</section>
<section className="col2">
<a>Read more {'>'}{'>'}</a>
</section>
</div>
))
}
</div>
I think this what you are after.
Sandbox
<section className="col1">
{Array.isArray(tags) ? (
tags.map((tag) => (
<span style={{ marginRight: "10px" }}>{tag}</span>
))
) : (
<span>{tags}</span>
)}
</section>
You could make this code much simpler be having the tags field always be an array even if there is a single element.

Saving and displaying "nested" JSON data from API

I have the following format of JSON data fetched from an API and stored in IntentList
{
"id": 22,
"name": "IntentName",
"fk_app": 3,
"fk_intent": null,
"nlu_models": [],
"sentences": [
{
"text": "text1",
"id": 2308
},
{
"text": "text2",
"id": 2309
},......
So there are these levels : the first having "name" and "sentences", and the second which is inside sentences, having "text".
My goal is to be able to search the API data By text and to display on each row the name and text found inside the sentences related to that text.
Therefore if I search "text" this would appear => IntentName text1 IntentName text2
if I search text1 this would appear => IntentName text1
-----What I have done/tried so far-----
I searched for corresponding text and stored its intent object and was only able to display its intent name
Typescript:
fetchIntentsTxt(event: any) {
if (event.target.value === '') {
return this.searchResultINTxt = [];
}
this.searchResultINTxt = this.IntentList.filter(
i => i.sentences.some(
s => s.text.toLowerCase().includes(event.target.value.toLowerCase())));
}
Html:
<input type="text" autocomplete="off" [(ngModel)]="searchInputINTxt" (keyup)="fetchIntentsTxt($event)"
class="form-control" id="search0" placeholder="Search for Intents by Text">
<a #myIntentTxt *ngFor="let intentTxt of searchResultINTxt" >{{intentTxt.sentences.text(this doesn't work obviously)}}<span
class="float-right">{{intentTxt.name}}</span> <br></a>
Any recommendation is appreciated, even if it meant changing the search function. Thanks in advance.
Example: https://stackblitz.com/edit/angular-ivy-pyaq7a
I would just create a new array in the fetchIntentsTxt method and add the data I want to display by the user input/search. After "filtering" all the data I need I set it to the variable thats iterated over in the View/Template.
public fetchIntentsTxt(searchValue: string): void {
const searchResults = [];
for (const entry of this.intentList) {
for (const sentence of entry.sentences) {
if (sentence.text.toLowerCase().includes(searchValue)) {
searchResults.push({
name: entry.name,
text: sentence.text,
});
}
}
}
this.searchResultINTxt = searchResults;
}
View:
<input type="text" autocomplete="off" [(ngModel)]="searchInputINTxt" (ngModelChange)="fetchIntentsTxt($event)" class="form-control" id="search0" name="search0" placeholder="Search for Intents by Text"/><br />
<a #myIntentTxt *ngFor="let intentTxt of searchResultINTxt">
{{ intentTxt.text }}<span class="float-right">{{ intentTxt.name }}</span>
<br/>
</a>
Also note here, I used the (ngModelChange) instead of the (keyup) on the search input so I do not need to hassle around with the events and just get the value I need for filtering.

Is there a method to display data from api where the id key is taken from another key?

JSON Data:
"abcd":[
{
"id":"1",
"cityId":"2",
},
{
"id":"2",
"cityId":"3",
}
],
"city":[
{
"id":"2",
"cityName":"california"
},
{
"id":"3",
"cityName":"vicecity"
}
]
Angular:
<div *ngFor="let clg of abcd$">
<p>
{{clg.cityId}}
<!-- Here i need to print the cityname from city by using the cityId we have got from abcd -->
</p>
</div>
app.component.ts:
ngOnInit() {
this.data.getColleges().subscribe(
data => this.abcd$ = data
)
}
fetching data from "abcd" is perfectly working....and no problem in fetching the datas from "city" too. But is it possible to fetch the cityName from "city" by using the cityId key from the "abcd" section.
You can use a method to get city by ID:
app.component.ts:
getCityByID = (cityID) => {
return this.city$.find(item => item.id === cityID);
}
Template:
<div *ngFor="let clg of abcd$">
<p>
{{ clg.cityId }}
{{ getCityByID(clg.cityId).cityName }}
</p>
</div>
Update
As far as I understand, you are fetching colleges and cities with 2 separate observables. Because of this, when you are trying to get city by ID, it may (or may not) throw an error if second observable has not been resolved yet. So, you need to combine/join these two streams together. I prefer combineLatest to do this but forkJoin will work as well.
In app.component.ts:
import { combineLatest } from 'rxjs';
......
ngOnInit() {
combineLatest(
this.data.getColleges(),
this.data.getCity()
).subscribe(([colleges, cities]) => {
this.abcd$ = colleges;
this.city$ = cities;
});
}
This way, you make sure that both abcd$ and city$ are inited. Hope this helps.
For further reading:
combineLatest: https://www.learnrxjs.io/operators/combination/combinelatest.html
forkJoin: https://www.learnrxjs.io/operators/combination/forkjoin.html

Angular orderBy object possible?

I have a JSON object representing calendar dates. These are added through a CMS and I'd like to be able to filter them based on date. My schema set-up has made this more difficult than I thought. Is it possible to orderBy the day value in this JSON object or is there a filter workaround?
Here is my JSON object:
{
"_id" : ObjectId("53f252537d343a9ad862866c"),
"year" : {
"December" : [],
"November" : [],
"October" : [],
"September" : [],
"August" : [],
"July" : [
{
"day" : "21",
"title" : "Event Title",
"summary" : "Event Summary",
"description" : "oEvent Description",
"_id" : ObjectId("53f252537d343a9ad862866d")
}
],
"June" : [],
"May" : [],
"April" : [],
"March" : [],
"February" : [],
"January" : []
},
"__v" : 0
}
Here is my view which already uses a custom filter to filter by month. The orderBy is not functioning but I've left it in as a placeholder to show where I'd like to set the functionality.
<div class="calDynamic" data-ng-repeat="n in [] | range:100">
<div ng-repeat="cal in calendar[n].year | filterKey:month">
<div ng-if="cal != '' ">
<div class="calendar">
<div ng-repeat="item in cal | orderBy: 'key.day' ">
<a href="/events/{{item.day}}">
<article class="eventslist">
<div class="numberedDate">
<h3>{{item.day}}</h3>
</div>
<div class="calInfo">
<h5>{{item.title}}</h5>
<p>{{item.summary}} <a>more</a></p>
</div>
</article>
</div><!-- ng-repeat val,key -->
</div><!-- calendar -->
</div><!-- ng-if cal -->
</div><!-- ng-repeat cal -->
</div><!-- calDynamic -->
You should be able to define a custom sort function that sorts by any item in your object. The key bit is to convert the object to an array in the filter function.
Here's an example:
app.filter('orderByDayNumber', function() {
return function(items, field, reverse) {
var filtered = [];
angular.forEach(items, function(item) {
filtered.push(item);
});
filtered.sort(function (a, b) {
return (a[field] > b[field] ? 1 : -1);
});
if(reverse) filtered.reverse();
return filtered;
};
});
You would then call it like this:
<div ng-repeat="(key, val) in cal | orderByDayNumber: 'day' ">
Note, you shouldn't write val.day as that is assumed.
Look at this great blog post here for more info.
EDIT: In fact, it looks like your structure is actually already an array, so while this technique will still work, it may not be necessary - it might have just been the way you were adding the parameter to orderBy that was causing issues.