How to remove property from Cheerio object? - node.js

I need to get all element except first with Cheerio. So I select all and then try to delete first but after when I try to loop elements I get error that first element undefined...
var categories = $('.subtitle1');
console.log(categories.length);
delete categories[0];
delete categories['0'];
console.log(categories.length);
categories.each(function(i, element) {
console.log(element.children);
});
Result:
15
15
TypeError: Cannot read property 'children' of undefined....
If I comment delete... everything works fine except that I have first element.

Maybe this could solve your problem:
var $element = $(<htmlElement>);
$element = $element.slice(1); // Return all except first.
// $element = $element.slice(1, $element.length);
Documentation: https://github.com/cheeriojs/cheerio#slice-start-end-
So in your case this should be work:
var categories = $('.subtitle1').slice(1);
categories.each(function(i, element) {
console.log(element.children);
});

Related

Parse returned element with nodeJS and Selenium

For the first time I'm trying Selenium and nodeJS. I need to parse HTML page and I'm having problem to parse returned element with XPATH again, to perform deeper search. Here is my code:
async function parse () {
let driver = await new Builder().forBrowser("firefox").build();
await driver.get("https://web-url.com");
await sleep(2000); //custom function
let elements = await driver.findElements(By.xpath("//ul[contains(#class, 'some-class')]//li"));
for (let i = 0; i < elements.length; i++) {
let timeContainer = await elements[i].findElement(By.xpath("//time[contains(#class, 'time-class')]")).getAttribute("datetime");
let innerHTML = await elements[i].getAttribute("innerHTML");
console.log(timeContainer);
}
await sleep(2000); //custom function
driver.close();
}
innerHTML variable returns correct HTML string for each element, but if I try to print timeContainer var, it prints same value for all elements. How to perform additional XPATH search for each element?
Thank you.
You need to make the XPath in this line elements[i].findElement(By.xpath("//time[contains(#class, 'time-class')]")) relative.
This is done by putting a dot . before the rest of XPath expression, as following:
elements[i].findElement(By.xpath(".//time[contains(#class, 'time-class')]"))
By default, when the page is scanned to find the match to the passed XPath expression it starts from the top of the page. This is why this line returns you the first (the same) match each time.
But when we put a dot . there, it starts searching inside the current node i.e. inside the elements[i] element.

Playwright: how to know which selector fired when waiting for multiple?

Imagine waiting for multiple selectors like this:
const element = await page.waitForSelector("div#test div#test2 div#test3 button, div#zest div#zest2 div#zest3 button")
How do I tell whether the first selector matched, or the second one? Do I need to $ twice and then compare the element?
You are using , separator which is the "multiple" operator.
To see which element you have, check the id of the parent.
const element = await page.waitForSelector("div#test div#test2 div#test3 button, div#zest div#zest2 div#zest3 button")
const parent = await element.$('xpath=..')
if (parent) {
const id = await parent.getAttribute('id')
console.log('id', id)
expect(id).to.eq('test3') // passes in test3 is present
// or if test3 is not present
expect(id).to.eq('zest3') // zest3 passes if test3 is not present
}
You can separate the selectors using a comma, which acts as an OR condition, and then extract the innerText of those elements. And then on the basis of the inner text, determine which selector was available.
const elementText = await page
.locator(
'div#test div#test2 div#test3 button,div#zest div#zest2 div#zest3 button'
)
.innerText()
if (elementText == 'some text') {
//Do something
}
In case you have an attribute-value pair that is unique to both, you can do like this:
const elementText = await page
.locator(
'div#test div#test2 div#test3 button,div#zest div#zest2 div#zest3 button'
)
.getAttribute('id')
if (elementText == 'zest') {
//Do something
}

JS filter or find an element in array where owner_name matches in react

I have got the two following arrays.
producers = { owner_name: "testing123, url: "http://test.com/", logo_svg: "http://test.com/logo.svg"}, { owner_name: "testing456, url: "http://test456.com/", logo_svg: "http://test456.com/logo.svg"}
result = { owner_name: "testing123, true, false}
I am trying to find a way to extract the logo_svg url from producers array when the owner_name match. I am very new to JS, so I'm not very good at it.
I manage to put this together but unclear on how I can get the logo_svg from that.
image={producers.filter((producer) => producer.owner_name === result.owner_name)}
try image = producers.find(p => p.owner_name == result.owner_name).logo_svg
but bear in mind that if the owner name does not exist in producers then find returns undefined and so you'll get an error so you can make the whole expression more robust by doing
obj = producers.find(p => p.owner_name === result.owner_name)
obj_url = obj ? obj.logo_svg || 'default_url_location'
image = obj_url
According to MDN, filter produces an array, so that is why you cannot get a valid variable inside your image.
You can try something like:
image={producers[producers.findIndex(el => el.owner_name === result.owner_name)]}
This will find the index of the item you are looking for and pass it immediately as the index in the producers array.

Office JS issue with recognising ListItems

I'm trying to add a paragraph at the end of the document and escape the possibility of the newly added paragraph to be added inside a list (if the document is ending with a list).
I have the following code:
let paragraph = paragraphs.items[paragraphs.items.length - 1]
let p = paragraph.insertParagraph('', window.Word.InsertLocation.after)
if (paragraph.listItemOrNullObject) {
p.detachFromList()
p.leftIndent = 0
}
The following happens: if there is a ListItem, the code works. If not, it breaks inside the if condition, like I wrote paragraph.listItem.
Shouldn't this be used like this?
EDIT - error thrown:
name:"OfficeExtension.Error"
code:"GeneralException"
message:"GeneralException"
traceMessages:[] 0 items
innerError:null
â–¶debugInfo:{} 4 keys
code:"GeneralException"
message:"GeneralException"
toString:function (){return JSON.stringify(this)}
errorLocation:"Paragraph.detachFromList"
the issue here is that the *.isNullObject methods/properties does not return a regular js 'null' object, but a NullObject (a special framework type of null).
check out this code i rewrote it i think in a more efficient way. excuse my js, you can port it to ts.
hope this helps.
Word.run(function (context) {
var listI = context.document.body.paragraphs.getLast().listItemOrNullObject;
context.load(listI);
return context.sync()
.then(function () {
if (listI.isNullObject) { // check out how i am validating if its null.
console.log("there is no list at the end")
}
else {
context.document.body.paragraphs.getLast().detachFromList();
context.document.body.paragraphs.getLast().leftIndent = 0;
return context.sync();
}
})
})
listItemOrNullObject will return a null object if it isn't a ListItem. Conceptually you're if is asking "if this is a list item or it isn't a list item" which effectively will also return true.
It is failing here you are attempting to detach from a non-existent list. I would take a look at isListItem. This will tell you specifically if the paragraph is a ListItem so you only execute p.detachFromList() when in fact it is part of a list.

SuiteScript 2 result set

I am writing a script in 2.0, here is the snippet.
var resultSet = [];
var result = search.load({
id:'customsearch_autosend_statement_invoice'
}).run().each(function( item ) {
resultSet.push(item);
});
If I run this saved search in the normal interface I get lots of rows like 2000 plus, but if I run this code I get back 1 row, even using the each function and adding the items to another array I only get one row. I don't see anything in the documentation about this. Can anyone tell me why this is? I'm stumped. thanks in advance for any help
I found the answer but not because I should have seen it, the examples don't make any attempt to tell you that you have to return true from the each method in order to keep receiving rows. So the answer is that at the end of the "each" function you must return a true value to receive the next row. Like this, so thanks for your effort if I miss your post.
var resultSet = [];
var result = search.load({
id:'customsearch_autosend_statement_invoice'
}).run().each(function( item ) {
resultSet.push(item);
return true;
});
It's detailed in the documentation, the callback function returns a boolean which can be used to stop or continue the iteration:
Use a developer-defined function to invoke on each row in the search
results, up to 4000 results at a time. The callback function must use
the following signature: boolean callback(result.Result result); The
callback function takes a search.Result object as an input parameter
and returns a boolean which can be used to stop the iteration with a
value of false, or continue the iteration with a value of true.
...
mySearch.run().each(function(result) {
var entity = result.getValue({
name: 'entity'
});
var subsidiary = result.getValue({
name: 'subsidiary'
});
return true;
});
...
ResultSet.each(callback)

Resources