XPath - retrieving the parent of a descendant that meets a criteria - xpath

Given the following structure:
A. body node as root
B. body contains only span nodes, call them top-level spans
C. top-level spans contains combinations of (1) text nodes (2) spans with class, call them lower level spans
D. lower level spans contains text nodes only
We need to retrieve each top-level span which have one or more lower level spans with classes "a" or "b".
Our best shot yet:
//span//span[contains(concat(' ', normalize-space(#class), ' '), ' qri ') or contains(concat(' ', normalize-space(#class), ' '), ' ktiv ')]
but it gives the lower level spans.
Demonstration:
x = new XPathEvaluator();
var it = x.evaluate("//span//span[contains(concat(' ', normalize-space(#class), ' '), ' a ') or contains(concat(' ', normalize-space(#class), ' '), ' b ')]", document.body.cloneNode(true));
var results = [];
for (var current = it.iterateNext(); current; current = it.iterateNext()) {
results.push(current.id);
}
console.log(results);
<span id="top1">In a<span id="a1" class="a">ultrices felis</span><span id="b1" class="b">sollicitudin felis</span></span>
<span id="top2">congue velit<span id="a2" class="a">Sed porttitor</span>Phasellus posuere</span>
<span id="top3">purus. Etiam<span id="b2" class="b">tellus, ultrices</span>Morbi interdum</span>
<span id="top4">leo malesuada tristique</span>
Desired output is:
[
"top1",
"top2",
"top3"
]

Try the following XPath
//span[.//span[#class = 'a' or #class='b']]
It selects all spans which have one or more span children of class a or b.
If you need it even more specific, add a parent:: axis
//span[parent::body and .//span[#class = 'a' or #class='b']]
Another possibility is an absolute path:
/body/span[.//span[#class = 'a' or #class='b']]

Related

Get all arguments after args[0] discord.js

I was trying to make a mute command, and I was adding a system where you can mute them for a reason. The bot would reply "(Username of user) was muted. Reason: (The reason)". For me args[0] is just mentioning the user that you want to mute, but I can't figure out how to get all after args[0]. I've tried doing something like this message.channel.send('I have muted' + (mutedUser) + 'Reason: ' + args[1++]. But that obviously didn't work - I was kind of guessing - I turned to listing 4 arguments like this.
message.channel.send('I have muted ' + taggedUser.user.username + ' Reason: ' + args[1] + ' ' + args[2] + ' ' + args[3] + ' ' + args[4])
But obviously, that's not very efficient - Does anyone know how to get all arguments after args[0]?
Take the array of args and slice() the number of arguments you want to delete, then join() the remaining array elements into a single string
Quick Tip
Use Template literals for easier formatting with strings and variables
const reason = args.slice(1).join(' ');
message.channel.send(`I have muted ${mutedUser}, Reason: ${reason}`);
You can use Array.prototype.join() and Array.prototype.slice()
const str = 'first second third fourth fifth sixth seventh...';
const args = str.split(' ');
console.log(args[0]); // first
console.log(args.slice(1).join(' ')); // everything after first
console.log(args[3]); // fourth
console.log(args.slice(4).join(' ')); // everything after fourth
// basically, `Array.prototype.join()` can join every element of an array (with an optional separator- in this case a space)
console.log(args.join(' ')); // join all elements with a space in between
// and `Array.prototype.slice()` can slice off elements of an array
console.log(args.slice(5)); // slice off 5 elements
// now you can combine these two :)
In a generic scenario, you can use destructuring assignment as so:
const [foo, ...bar] = args;
Here, foo is equal to args[0] and the rest of args is contained within bar as an array.
Specific to your case you could probably do this within your command:
const [mutedUser, reason] = args;
And then as Elitezen suggested, use template literals to send your message.
message.channel.send(`I have muted ${mutedUser}, Reason: ${reason}`);

Xpath: Find element with class that contains spaces

So I have elements that look like this
<li class="attribute "></li> # note the space
<li class="attribute"></li>
Using the xpath //li[#class="attribute"] will get the second element but not the first. How can I get both elements with the same xpath?
This XPath 1.0 expression,
//li[contains(concat(' ', normalize-space(#class), ' '),
' attribute ')]
will select all li elements with class attributes that contain the attribute substring, regardless of whether it has leading or trailing spaces.
If you only want to match attribute with possible leading and trailing spaces only (no other string values), just use normalize-space():
//li[normalize-space(#class) = 'attribute']

Searching a list and displaying whether or not the list contains the search variable

I have a Twitter manager program where one option is searching for tweets containing whatever input the user provides. I am unable to figure out a way to have the for loop skip the else statement when there is a match found in the list. As it works now, the program will print the tweet it finds that matches the search criteria, but also prints that no tweet is found containing the search criteria.
# Option 3, search tweet_list for specific content and display if found
# in descending order starting with the most recent.
elif count == 3:
tweet_list.reverse()
if len(tweet_list) == 0:
print('There are no tweets to search.', '\n')
else:
search = input('What would you like to search for? ')
print('\nSearch Results')
print('--------------')
for n in tweet_list:
a = n.get_author()
b = n.get_text()
c = n.get_age()
if search in a or search in b:
print(a + '-' + c + '\n' + b + '\n')
else:
print('No tweets contain "' + search + '".' + '\n')
print('')
Create a variable, say found, and initialize it to False. When you find a tweet, set it to True. Then replace the else: with if not found:.

How to join the results of two XPath expressions using the concat function?

I have following XML:
<root>
<chp id='1'>
<sent id='1'>hello</sent>
<sent id='2'>world</sent>
</chp>
<chp id='2'>
<sent id='1'>the</sent>
<sent id='2'>best</sent>
<sent id='3'>world</sent>
</chp>
</root>
Using the XPath expression
root/chp/sent[contains(.,'world')]/#id
I have the result 2 3 (which is exactly what I want), but when I run
concat('sentence ', /root/chp/sent[contains(.,'world')]/#id, ' - chap ' , /root/chp/sent[contains(.,'world')]/../#id )
the result breaks at the first result:
sentence 2 - chap 1
The last argument does not contain a single value, but a sequence. You cannot use XPath 1.0 to join this sequence to a single string. If you're able to use XPath 2.0, use string-join($sequence, $divisor):
concat(
'sentence ',
string-join(/root/chp/sent[contains(.,'world')]/#id, ' '),
' - chap ',
string-join(/root/chp/sent[contains(.,'world')]/../#id, ' ')
)
which will return
sentence 2 3 - chap 1 2
Most probably you want to loop over the result set yourself (also requires XPath 2.0):
for $sentence in /root/chp/sent[contains(.,'world')]
return concat('sentence ', $sentence/#id, ' - chap ', $sentence/../#id)
which will return
sentence 2 - chap 1
sentence 3 - chap 2
If you cannot use XPath 2.0, but are able to further process results in some outside programming language (like Java, PHP, ...) using DOM: Use /root/chp/sent[contains(.,'world')] to find the sentence nodes, loop over them, then query the #id and parent (chapter) #id using DOM and construct the result.

In xpath how you compare text() with \r\n (line break)?

I want to get the node :
//script[starts-with(text(). '\r\nvar name')]
but it seems xpath does not recognize \r\n escape characters. Any ideas how to match them?
Note: I am using html agility pack
Use:
//script[starts-with(., '
var name')]
Most often XML is normalized by the XML parser and there is only a single NL character left -- therefore, if the above expression doesn't select the wanted script elements, try with:
//script[starts-with(., '
var name')]
Or, this would work in both cases:
//script
[(starts-with(., '
') or starts-with(., '
'))
and
starts-with(substring-after(., '
'), 'var name')
]

Resources