How to get indexOf to operate properly after removal of the prefix from Data?

A simple example in Swift 4:

var data = Data([0, 1, 2, 3, 4, 5])
data.removeFirst(5)
let index5 = data.index(of: 5) // = 5 !!!
let count = data.count // = 1

Somehow index5 equals 5, although the data remains only 1 element.

Also, if you do so:
var subdata = data.suffix(1)
let index5s = subdata.index(of: 5) // = 5
let countS = subdata.count // = 1
the result is the same.

How to get the correct index of the element when calling indexOf?
June 10th 19 at 14:49
1 answer
June 10th 19 at 14:51
Solution
In the description the method index(of: )
Discussion
Attempting to remove more elements than exist in the collection triggers a runtime error.

Calling this method may invalidate all saved indices of this collection. Do not rely on a previously stored index value after altering a collection with any operation that can change its length.


How can this be avoided? Using these techniques, any way. Use say filter:
import Foundation

var data = Data([0, 1, 2, 3, 4, 5])
var newData = data.filter{$0 < 5}
print(newData.index(of: 5) ?? "Ooops") // Ooops


Generally avoid all method that reduce the length of the array in Data.
I have a simple problem, data is received from URLSessionDataTask, I add to the Data object method append.
If Data is a newline character, I this symbol is fed further, and cut the Data front this symbol. (That is the usual row-by-row reading).

Data.filter can and will do the job, but he has complexity O(n), and removeFirst O(1). - Kayley.Gutmann10 commented on June 10th 19 at 14:54
,
var linesOfData = [Data]()
repeat {
 let tmp: Data = getNewData()
 let index = tmp.index(of: ...)
 linesOfData.append(tmp.prefix(upTo: index))
// ...
} while(...)

func prefix(upTo end: Int) -> Data
Complexity: O(1) - alycia_Pfannerstill commented on June 10th 19 at 14:57
It will definitely not work. At least the error that linesOfData only added one line of received data getNewData(); and there may be potentially 1000.
The second mistake that lost the tail after the element index, which would be fastened in front to the next chunk of data. - Kayley.Gutmann10 commented on June 10th 19 at 15:00
I can't understand. You wanted complexity O(1) you got it. There is nothing clipped and indices are stored, I have to put something ... . Do then what You need.
in linesOfData only added one line of received data getNewData();
that kind of nonsense? It was just an example. Doblaje in the array exactly as you need. - alycia_Pfannerstill commented on June 10th 19 at 15:03
The point is that the method prefix(upTo: index) is O(1) and it does not hurt anything and does not touch the indices. After inserting the prefix, you can continue to work with the data. What's wrong that? - alycia_Pfannerstill commented on June 10th 19 at 15:06
Well, honestly I do not see anything complicated?
For the first part look up the index tmp.index(of: ...) and find the first occurrence of "\n". Fast O(1) use a prefix(upTo end: Int) save the line.
Then again we yuzaem knowing the index suffix(from start: Int) from the original line, which also runs in O(1). and so on - alycia_Pfannerstill commented on June 10th 19 at 15:09
Not that this method doesn't work for several reasons, I showed why.

In working code, just need to glue the pieces of data.

Again, this occurs: getNewData() provides a piece of data in a random order there are multiple delimiters (byte code 10).
The output linesOfData should contain pieces that are between the delimiters (and such pieces can be from different calls to getNewData() ) - Kayley.Gutmann10 commented on June 10th 19 at 15:12
I'm just showed you how it's done. Where are you looking???
index(of: ...) to search for the 1st occurrence of "\n"
prefix(upTo: ...) for 0(1) --> kept in an array of strings 1st string
next, look for the index(of: ...) in the suffix(from: ...) at the same index that yuzali prefix(upTo: ...) and again 0(1) we are looking for a new entry and add a 2nd line, and so on. The message is clear? Nothing is lost... - alycia_Pfannerstill commented on June 10th 19 at 15:15
,
var linesOfData = [Data]()
let tmp: Data = getNewData()

let index1 = tmp.index(of: ...)
linesOfData.append(tmp.prefix(upTo: index1)) /// 1

let index2 = tmp.suffix(from: index).index(of: ...)
linesOfData.append(tmp.suffix(from: index).prefix(upTo: index2)) /// 2

and so on
all this can be nicely wrapped in a function

here is a real bummer just for the fact that you want O(1)

THIS is JUST an EXAMPLE and this idea can be nicely wrapped in a function which will run across the line which spat out the function getNewData()
- alycia_Pfannerstill commented on June 10th 19 at 15:18
This method works, it uses a counterintuitive let index2 = tmp.suffix(from: index).index(of: ...) , which returns the position relative to the beginning of tmp and not tmp.suffix().

(Actually about this design was the question topic)

But it kompensiruet that prefix(upTo: index2) also works from the beginning of tmp, not the argument of the substring.

In General, it works, but the brain must rebuild. - Kayley.Gutmann10 commented on June 10th 19 at 15:21
welcome. thank the magic button. - alycia_Pfannerstill commented on June 10th 19 at 15:24
, the code was purely in his head called. And small problems were related to the fact that I forgot a +1 to the index add. But in General the code is correct. That's completely fixed:
import Foundation

var linesOfData = [Data]()
let tmp: Data = Data([1, 2, 3, 5, 3, 2, 1, 5])

let index1 = tmp.index(of: 5) 
linesOfData.append(tmp.prefix(upTo: index1!)) 

let index2 = tmp.suffix(from: index1! + 1).index(of: 5)
linesOfData.append(tmp.suffix(from: index1! + 1).prefix(upTo: index2!)) 

print(linesOfData) // [3 bytes 3 bytes]
- alycia_Pfannerstill commented on June 10th 19 at 15:27
Here is the result of my working code:

private var buffer = Data()

func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
buffer.append(data)

 var lines = buffer.split(separator: 10, maxSplits: Int.max, omittingEmptySubsequences: false)

 guard lines.count > 0 else {
return
}

 buffer = lines.popLast()!

 for line in lines {
proceedLine(line)
}
}
- Kayley.Gutmann10 commented on June 10th 19 at 15:30
asked for O(1) ... do not understand. - alycia_Pfannerstill commented on June 10th 19 at 15:33
This method of solving the problem described in my first comments:
"I have a simple problem, data is received from URLSessionDataTask, I add to the Data object method append.
If Data is a newline character, I this symbol is fed further, and cut the Data front this symbol. (That is the usual row-by-row reading)."

Show that this can be done faster.

(although there is of course a possible small optimization, not buffer.append(data) call, and do the concatenation buffer and lines[0] and putting it in lines[0])

Of course, 0(1) meant that when comparing the performance removeFirst (O(1)) and filter (O(n))
In solving this problem entirely you will never achieve O(1) as in any case you will cycle.

In General, if you write significantly faster code — it will be interesting. - Kayley.Gutmann10 commented on June 10th 19 at 15:36
Here is an easy optimization (although I don't know how fast is Data.append, maybe there is Apple written so that the data is not actually copied, then the optimization is not needed)

private var buffer = Data()

func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {

 var lines = data.split(separator: 10, maxSplits: Int.max, omittingEmptySubsequences: false)

 guard lines.count > 0 else {
return
}

 if !buffer.isEmpty {
buffer.append(lines[0])
 lines[0] = buffer
}

 buffer = lines.popLast()!

 for line in lines {
proceedLine(line)
}
}
- Kayley.Gutmann10 commented on June 10th 19 at 15:39

Find more questions by tags Swift