|
Hi there, I'm slowly learning SwiftUi online and I've gotten stuck! I'm trying to sort some messy data that I get from an outside source (and I don't have control over) to make it usable in my app. this is what is looks like when I get it | 559a| 649a| 705a| 943a|1234p| 321p| 629p| 857p|1001p| 658a| 827a|1108a|156p| 456p| 748p|1201x| Each number in the string represents a time on a schedule with 'a' for AM, 'p' for PM, and 'x' for AM the next day i want to be able to past the above into a text feild in the app (this part I know how to do), then remove the '|' symbol and use the string to create an array based on them I then want to sort them in the array chronologically (sorted both by the number itself then a/p/x) I don't need to be able to access them as actual dates/times, just strings to feed into a ForEach list Output would look like this: sortedSchedule = ["559a", "649a", "658a", "705a", "827a", "943a", "1108a", "1234p", "156p", "321p", "456p", "629p", "748p", "857p", "1001p", "1201x"] I've tried using string.split(separator: "|") and can get that sybmbol out, but i'm really struggling to figure out the sorting part. here's what I've been testing with
I'm not sure if there's a better way to do this, but any direction would be helpful! |
|
Here's what I came up with. Noon and midnight proved a wee bit tricky, but it wasn't that bad.
|
|
@rooster provides an excellent example. But to boost your Swift skills, take a moment to consider one of Swift's latest powertools: Regular ExpressionsSearching, extracting, and manipulating data is a core skill for most software engineers. By the way, the "G" in A famous quote goes "I tried to solve a problem with Regex. Now I have two problems." Yeah, Structure Your DataIn the spirit of taking a big problem and breaking it down into smaller, solvable problems, the first thing you might consider is the structure of the data you want to end up with. In the code below, I created an See -> Eating an Elephant Find the Pattern. Define it as a Regular ExpressionLook at the data you provided. Then look closely for a pattern that you can recognize. I won't attempt to show you what the old school regular expression might look like 🤮. Instead, I used the more expressive
ResultsIf you run this code in Playgrounds it shows:
Because the parsed parts are in a Keep Coding |
SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates. Sponsor Hacking with Swift and reach the world's largest Swift community! |
|
Wow! @roosterboy @Obelix, thank you so much. One thing I'm continously learning on my learning journey is how much more there is to learn. It is definitley daughting at times, but has also been very empowering and exciting. I really do appreciate you both taking the time to help workshop this. @roosterboy This is perfect! I continue to find sorting hard to wrap my head around, so I appreciate you breaking it down. This along with @Obelix's Regex/struct suggestions are amazing. Some thoughts in response to your answer, @Obelix Firstly, thank you for breaking things down so clearly and providing so much context. Regular Expressions I'm new to coding as a whole and never really delt with Regex! Now that I've seen an example of how powerful it can be I'll definitely be doing some digging. The app I'm trying to build is related to my job and I have a ton of ugly data, like what I provided in my example, so Regex will likely be an immensly useful tool. Also thank you for the context of how challenging Regex can be - one thing I've really appreciated over the past year of learning Swift/SwiftUI is how much effort has clearly gone into simplifying and making coding more accessible (effort that clearly continues - yay SwiftData!!!!!!!). Structuring your data These times will become the very last piece of data in my overall data structure so I hadn't thought to break this down even further into a struct. You're example clearly highlights the value of doing so though! I've always been a bit nervous to post in the forums, so I cannot express enough how thankful I am to you both for your help and direction. You've both given me lots to consider and dig into over the next few days. Happy coding! |
|
Follow UpAfter posting, I bounced a few more ideas around. You may want to capture malformed data ( 0812g|1022m| ) so you can highlight this to fix in your authoritative datastore. On the otherhand, you may just want to ignore these, but the current pattern includes them. Evaluate the PatternThis goes back to the step where you need to document the pattern and turn it into a regular expression. The current pattern is defined as:
Notice the pattern indicates capturing In your data, you only want to find patterns that have a very particular ending, namely a, m, or x. Improved PatternYour data field may contain thousands of event times, or more. But you are only interested in patterns that end with a, m, or x. Redefine the regular expression pattern to indicate you're only interested in this particular pattern. Ignore all other patterns. For example 1122m|. and 023W| seem to have times and meridiems, but these fail the pattern test and are ignored.
|
|
As cool as regular expressions can be, they are really overkill for this situation. All you need is some simple string manipulation.
Aw, it's not that bad:
|
|
Maybe you're helping me make my point?
There are two nice Regex parsers online, swiftregex.com and regex101.com Your pattern only works (?) if the input string has one, and only one, entry and the entry doesn't end with the pipe character.
I think the caret charater (^) locks the pattern to the start of the string. And the $ locks it to the end of the string. So this Regex pattern only finds time events with 3 or 4 digits, the letter a, p, or x that is the only pattern in the input string. Add more data, and pipe separaters, and the expression fails. We want to find this pattern anywhere in the long string. So, don't lock the pattern to the start of the string. Nor lock it to the end of the string. The modified pattern below seems to work in both websites with the sample data below:
Both online parsers show 12 matches with these adjustments. SwiftRegex.com ParserWhat's nice about the swiftregex.com parser is how it creates the dynamic Swift Regex builder code from the old school Regex code. So you can see that this -> becomes this:
Sweet! I prefer Swift's more declarative approach! Keep Coding! |
|
That pattern isn't for checking the entire string but for the pieces that have previously been split on the It is a nice exercise, though. The new |
|
I agree with you. Applying regular expression pattern matching to (potentially) thousands of pieces previously parsed with string manipulation doesn't make sense. Instead, I suggested applying the pattern to the entire string to harvest the pieces. In this approach, I suggested the old school regular expression (with carets, parentheses, brackets, and braces) was barf-worthy. I think you proved this point providing an expression that didn't work on the entire string, but on a single extraction. If you have (potentially) thousands of individual pieces, I agree it's overkill to apply a regular expression thousands of times. OP followed up stating
He said it himself. He has a ton of ugly data to deal with. Applying one pattern to tons of data, that might be a different story. |
|
I'm loving this discussion around what is most effective! It's very eye opening and I think both have their place in my app. The initial example string I provided is from a PDF and copy/pasting it into the app to extract and display the specific data for a single 'shift' was the limit of my thinking, based on my limited knowledge, when I made the post. That is to say, if the pdf has 100 shifts, I'd be copy/pasting each shift individually. However, the information you shared @Orbelix on Regex really opens up possibilities - I can expand it to parse through multiple days at a time by building out my model structure and the regex. Easier said than done obviously, but a new avenue to explore that I wouldn't have considered otherwise. And yeah, so much other ugly data, this example was the least ugly amongst the heap 🤢 @roosterboys' code would likely cover me for the present version of my app, but the combo of both is a game changer for future interations as I learn more! happy Coding! p.s. this is what a full 'shift' looks like copy/pasted with no editing or clipping on my part
|
|
@ErinJulius posts
I pasted this shift data into the Playgrounds variable The regular expression parsed this hodge-podge of codes and data and found 22 matching patterns, ignoring all the other data. Results:
|
|
Okay, with this expanded data set, a regular expression is probably a better way to approach this. With the data given in the original problem statement, I would not go that way. |
|
Hey @Obelix, I'm playing around with the code that you provided in a playground and noticed a slight error (that provided some lessons in troubleshooting 😅) the code threw errors for all of the meridiems but after some trial and error I realized that the initializer for self.meridiem was causing the errors:
this line in particular:
replaced "String" with meridiem and it ran perfectly!
Thank you again for the help and direction! I'll be having some fun with it and regex moving forward with my app! |
SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.
Sponsor Hacking with Swift and reach the world's largest Swift community!
You need to create an account or log in to reply.
All interactions here are governed by our code of conduct.
Link copied to your pasteboard.