0

I’m trying to get the index of an element contained in a certain class that matches a global variable. Searching for nodes with CSS queries gives me a list with potential matching elements, but — as Kanna’s documentation points out — such queries return XPathObjects that act as arrays. The only way I can see to get index values from said list is to convert it from an array into a string, which can then be split by new lines; however, I can’t seem to get the list to take a string value. The relevant part of a typical compile session log follows:

Kazuo®/Ishiguro® Auprès® (2 pack)
Orange
Kazuo®/Ishiguro® Auprès® Folio Toujours (2 Pack)
Blue
…

I’ve tried three methods suggested by one poster in this thread to concatenate the above output:

1). componentsSeparatedByCharactersInSet

for node in (doc?.css("a[class^='product-link']"))! {
    let multiLineString = node.text!
    let newlineChars = NSCharacterSet.newlineCharacterSet()
    let lineArray = multiLineString.componentsSeparatedByCharactersInSet(newlineChars).filter{!  $0.isEmpty}
}   

Ideally, this would print [Kazuo®/Ishiguro® Auprès® (2 pack), Orange, Kazuo®/Ishiguro® Auprès® Folio Toujours (2 Pack), Blue]; it raises an error instead. Clicking fix leads to another error — and another one.

2). split

for node in (doc?.css("a[class^='product-link']"))! { 
    let multiLineString = node.text!
    let newlineChars = NSCharacterSet.newlineCharacterSet()
    let lineArray = multiLineString.utf16.split { newlineChars.characterIsMember($0) }.flatMap(String.init)
}

Yields the same result as componentsSeparatedByCharactersInSet: Cannot call value of non-function type 'CharacterSet' -> fix -> error -> fix -> error.

3). enumerateLines

for node in (doc?.css("a[class^='product-link']"))! {             
    let multiLineString = node.text!
    var lineArray = [String]()
    multiLineString.enumerateLines { (line, stop) -> () in
        lineArray.append(line)
    }
}

This solution actually builds, but it returns each list item as function().

These methods work when I try them on simple multi-line string literals in Playgrounds, but for some reason they don't work on the output above. The easiest way to solve this problem would be to use func index(of element: Element) -> Int?, but doing so gives me a Cannot convert value of type 'String' to expected argument type 'Character' error. I'm new to Swift, so if anyone with more experience could suggest alternative approaches to this problem, I would much appreciate the help!

solo
  • 743
  • 2
  • 6
  • 17

3 Answers3

1

Regarding your larger goal, this is how you should go about solving the problem:

var prodIndex = 0
var testProd = "Prod" // to be replaced by user input

for node in (doc?.css("a[class^='name-link']"))! {
    let words = testProd.split(separator: " ")
    if prodIndex % 2 == 0 {
        if node.text! == testProd {
            print("FOUND: " + testProd)
        }

        prodIndex += 1
    }
}
ch1maera
  • 1,369
  • 5
  • 20
  • 42
0

The code is outdated. The first method in Swift 3+ is

let newlineChars = CharacterSet.newlines
let lineArray = multiLineString.components(separatedBy: newlineChars).filter{ !$0.isEmpty }

To put the text in one line use

let oneLine = multiLineString.components(separatedBy: newlineChars).filter{ !$0.isEmpty }.joined(separator: " ")
vadian
  • 274,689
  • 30
  • 353
  • 361
  • This gives me: `["Kazuo®/Ishiguro® Auprès® (2 pack)"]\n["Orange"]\n["Kazuo®/Ishiguro® Auprès® Folio Toujours (2 Pack)"]\n["Blue"]`. How do I get them on the same line? Splitting by newline just replicates the issues demonstrated above. – solo Jan 01 '18 at 06:16
0

I think you are trying to say you want

Kazuo®/Ishiguro® Auprès® (2 pack)
Orange
Kazuo®/Ishiguro® Auprès® Folio Toujours (2 Pack)
Blue

as a single line string correct?

for node in (doc?.css("a[class^='product-link']"))! {
    // suggestion: use a guard here, it's good practice to safely unwrap optionals
    guard let multiLineString = node.text else { return }

    let lineArray = multiLineString.components(separatedBy: .newlines).joined(separator: " ")
 }

prints: Kazuo®/Ishiguro® Auprès® (2 pack) Orange Kazuo®/Ishiguro® Auprès® Folio Toujours (2 Pack) Blue

mmr118
  • 496
  • 4
  • 14
  • Hmm, for some reason I'm still getting my original output of `Kazuo®/Ishiguro® Auprès® (2 pack)\nOrange\nKazuo®/Ishiguro® Auprès® Folio Toujours (2 Pack)\nBlue`. Could this be a system issue? – solo Jan 01 '18 at 10:08
  • Try switching `.newlines` with `”\n”` – mmr118 Jan 01 '18 at 10:10
  • Hmmm, there maybe something missing from the question that isn’t obvious out because nothing is standing out to m on why this should’ve work. – mmr118 Jan 01 '18 at 19:24
  • How are you handling `lineArray` once it’s assigned? – mmr118 Jan 01 '18 at 19:26
  • When I run `print(lineArray)` it gives me the same output as above. – solo Jan 01 '18 at 19:38
  • It is actually really bugging me that this is not working. If you want, my twitter is listed on account, if you want you can direct message me there and we can try a more in detail approach to this. – mmr118 Jan 01 '18 at 22:20