I'm trying to parse string with date format like MM/YY and store month and year variables.
I wrote this code and can't figure out why when I pass string like "1":
match.numberOfRanges == 3
match.rangeAtindex(2) == (9223372036854775807,0)
Here is my code (regex has only two groups, so I don't understand how number of ranges can be even theoretically more than 2).
let regex = NSRegularExpression(pattern: "^(\\d{1,2})?[\\s/]*(\\d{1,2})?", options: NSRegularExpressionOptions.allZeros, error: nil)
// Expiry date string is "1"
let match = regex?.firstMatchInString(expiryDate, options: NSMatchingOptions.allZeros, range: NSMakeRange(0, expiryDateNS.length))
if let match = match {
let monthRange = match.rangeAtIndex(1)
// next string works correct - month contains "1"
var month = expiryDateNS.substringWithRange(monthRange)
if match.numberOfRanges > 1 { // match.numberOfRanges returns 3
let yearRange = match.rangeAtIndex(2) // returns LONG_MAX as location, 0 as length
// next line will crash
expiryYear = expiryDateNS.substringWithRange(yearRange)
}
}
UPDATE
As #matt asked, I'm adding few examples here.
String "1" should be parsed and stored as month == "1" and expiryYear == ""
String "12" should be parsed and stored as month == "12" and expiryYear == ""
String "12/45" should be parsed and stored as month == "12" and expiryYear == "45"
When I'm parsing string "1" with code above match.numberOfRanges is 3 and match.rangeAtindex(2) is (9223372036854775807,0)
For the input string "1", the second capture group (\\d{1,2})? is matched
zero times. In that case
match.rangeAtIndex(2).location is NSNotFound (which happens to be
Int.max = 9223372036854775807).
For the input string "/12" the first capture group (\\d{1,2})? would
be matched zero times. So you have to check for this cases:
var month = ""
var year = ""
if let match = match {
let monthRange = match.rangeAtIndex(1)
if monthRange.location != NSNotFound {
month = expiryDateNS.substringWithRange(monthRange)
}
let yearRange = match.rangeAtIndex(2)
if yearRange.location != NSNotFound {
year = expiryDateNS.substringWithRange(yearRange)
}
}
For such a simple string and pattern, NSScanner is easier. This function gives the outputs you specified for the inputs you specified:
func analyze(s:String) -> (String,String) {
var result = ("","")
let sc = NSScanner(string: s)
var first:Int32 = 0
let ok = sc.scanInt(&first)
if ok {
result.0 = String(first)
let ok = sc.scanUpToCharactersFromSet(NSCharacterSet.decimalDigitCharacterSet(), intoString: nil)
if !sc.atEnd {
var second:Int32 = 0
let ok = sc.scanInt(&second)
if ok {
result.1 = String(second)
}
}
}
return result
}
So if you decide to split your string you can do as follow:
let date = "12 / 45".stringByReplacingOccurrencesOfString(" ", withString: "", options: .LiteralSearch, range: nil)
let components = date.componentsSeparatedByString("/")
let month = components.count > 0 ? components.first! : ""
let expiryYear = components.count > 1 ? components.last! : ""
Related
this is the problem
A phrase is a palindrome if, after converting all uppercase letters into lowercase letters and removing all non-alphanumeric characters, it reads the same forward and backward. Alphanumeric characters include letters and numbers.
Given a string s, return true if it is a palindrome, or false otherwise.
Example 1:
Input: s = "A man, a plan, a canal: Panama"
Output: true
Explanation: "amanaplanacanalpanama" is a palindrome.
Example 2:
Input: s = "race a car"
Output: false
Explanation: "raceacar" is not a palindrome.
myCode
class Solution {
fun isPalindrome(s:String):Boolean {
var s1 = s.toLowerCase()
var myStringBuilder = StringBuilder()
var n = s1.length-1
var n1=myStringBuilder.length
for ( i in 0..n) {
if (Character.isLetterOrDigit(s1[i])) {
myStringBuilder.append(s1[i])
}
}
for( i in 0 .. (n1/2)-1){
if(myStringBuilder[i] != myStringBuilder[n1-i-1]){
return false
}
}
return true
}
}
the first case passed
but this is not passed as per the result Input: s = "race a car result true expected is false
You're initialising n1 too early:
// create an -empty- StringBuilder
var myStringBuilder = StringBuilder()
...
// since it's empty, n1 == 0
var n1=myStringBuilder.length
You're setting it to the length of the StringBuilder contents before you've actually put anything in it. This is a simple value you're setting, it's not a reference to the length getter that will give the current value when you access it. You set it once and that's its value forever.
So your last loop, the one that checks if it's a palindrome or not, never actually runs:
// since n1 is 0, this is for (i in 0..0)
for( i in 0 .. (n1/2)-1){
You can fix it by initialising n1 when you've finished adding your content to the StringBuilder, so you can get its final length:
for ( i in 0..n) {
if (Character.isLetterOrDigit(s1[i])) {
myStringBuilder.append(s1[i])
}
}
// StringBuilder is complete, grab its final length
var n1 = myStringBuilder.length
// now you can use it
for (i in 0..(n1/2)-1) {
Just fyi, there's also an until operator that works like .. except it doesn't include the last value of the range. So you can write
for (i in 0 until (n1/2))
if you want!
You can use this simple solution.
fun isPalindrome(s:String):Boolean {
val str = s.filter { it.isLetterOrDigit() }.lowercase()
for (i in 0..str.length/2 ){
if (str[i]!=str[str.length-i-1])
return false
}
return true
}
Edit:
By the #cactustictacs comment, you can do this in much more simple way.
fun isPalindrome(s:String):Boolean {
val str = s.filter { it.isLetterOrDigit() }.lowercase()
return str == str.reversed()
}
The problem I'm working on accepts a number string and will output the product of the odd or even numbers in the string. While the product of purely number string is working fine, my code should also accept strings that is alphanumeric (ex: 67shdg8092) and output the product. I'm quite confused on how I should code the alphanumeric strings, because the code I have done uses toInt().
Here's my code:
fun myProd(Odd: Boolean, vararg data: Char): Int {
var bool = isOdd
var EvenProd = 1
var OddProd = 1
for (a in data)
{
val intVal = a.toString().toInt()
if (intVal == 0)
{
continue
}
if (intVal % 2 == 0)
{
EvenProd *= intVal
}
else
{
OddProd *= intVal
}
}
if(bool == true) return OddProd
else return EvenProd
}
Use toIntOrNull instead of toInt. It only converts numeric string
val intVal = a.toString().toIntOrNull()
if (intVal == null || intVal == 0) {
continue
}
Starting from Kotlin 1.6 you can also use a.digitToIntOrNull().
P.S. Your method could be also rewritten in functional style
fun myProd(isOdd: Boolean, input: String): Int {
return input.asSequence()
.mapNotNull { it.toString().toIntOrNull() } // parse to numeric, ignore non-numeric
.filter { it > 0 } // avoid multiplying by zero
.filter { if (isOdd) it % 2 != 0 else it % 2 == 0 } // pick either odd or even numbers
.fold(1) { prod, i -> prod * i } // accumulate with initial 1
}
I am trying to use translation string in my adapter but it returns numbers instead
if(currentItem.budget != null){
holder.budget.text = "$ " + currentItem.budget.format()
} else {
holder.budget.text = R.string.open_to_suggestions.toString()
}
R.string.open_to_suggestions.toString() supposed to return string text Open to suggestions but it returns numbers such as 2131755113 not sure why! any idea?
To show the string resource you must use context.getString()
if(currentItem.budget != null) {
holder.budget.text = "$ " + currentItem.budget.format()
} else {
val context = holder.itemView.context
holder.budget.text = context.getString(R.string.open_to_suggestions)
}
Please take a look at the definition of getString here
I have created a 'SiteObject' which includes the following fields:
data class SiteObject(
//Site entry fields (10 fields)
var siteReference: String = "",
var siteAddress: String = "",
var sitePhoneNumber: String = "",
var siteEmail: String = "",
var invoiceAddress: String = "",
var invoicePhoneNumber: String = "",
var invoiceEmail: String = "",
var website: String = "",
var companyNumber: String = "",
var vatNumber: String = "",
)
I want to filter an ArrayList<SiteObject> (call it allSites) by checking if any of the fields of the objects within the list match those in a specific <SiteObject> (call it currentSite).
So for example, I know how to filter looking at one field:
fun checkIfExistingSite(currentSite: SiteObject) : ArrayList<SiteObject> {
var matchingSites = ArrayList<SiteObject>()
allSites.value?.filter { site ->
site.siteReference.contains(currentSite.siteReference)}?.let { matchingSites.addAll(it)
}
return matchingSites
}
But I am looking for an elegant way to create a list where I compare the matching fields in each of the objects in allSites with the corresponding fields in currentSite..
This will give me a list of sites that may be the same (allowing for differences in the way user inputs data) which I can present to the user to check.
Use equals property of Data Class:
val matchingSites: List<SiteObject> = allSites
.filterNotNull()
.filter { it.equals(currentSite) }
If you are looking for a more loose equlity criteria than the full match of all fields values, I would suggest usage of reflection (note that this approach could have performance penalties):
val memberProperties = SiteObject::class.memberProperties
val minMatchingProperties = 9 //or whatever number that makes sense in you case
val matchingItems = allSites.filter {
memberProperties.atLeast(minMatchingProperties) { property -> property.get(it) == property.get(currentSite) }
}
fun <E> Iterable<E>.atLeast(n: Int, predicate: (E) -> Boolean): Boolean {
val size = count()
return when {
n == 1 -> this.any(predicate)
n == size -> this.all(predicate)
n > size - n + 1 -> this.atLeast(size - n + 1) { !predicate.invoke(it) }
else -> {
var count = 0
for (element in this) {
if (predicate.invoke(element)) count++
if (count >= n) return true
}
return false
}
}
}
you could specify all the fields by which you want to match the currentSite inside the filter predicate:
fun checkIfExistingSite(currentSite: SiteObject) =
allSites.filter {
it.siteAddress == currentSite.siteAddress
|| it.sitePhoneNumber == currentSite.sitePhoneNumber
|| it.siteReference == currentSite.siteReference
}
Long but fast solution because of short circuiting.
If the list is nullable you can transform it to a non nullable list like:
allSites?filter{...}.orEmpty()
// or imho better
allSites.orEmpty().filter{...}
Trying to get a total amount of time from a start time and end time.
I have the DateFormatter correct as in Extension.swift below but I am getting confused on the way to calculate bottomtime - starttime.
Any help is appreciated. Thanks
Extension.swift
extension NSDate{
var bottomtimestringValue: String{
return self.toString()
}
func tobottomtimeString() -> String {
let formatter = NSDateFormatter()
formatter.dateFormat = "yyyy-MMM-dd"
let str = formatter.stringFromDate(self)
return str
}
}
extension String{
var bottomtimedateValue: NSDate?{
return self.toDate()
}
func tobottomtimeDate() -> NSDate? {
let formatter = NSDateFormatter()
formatter.dateFormat = "yyyy-MMM-dd"
if let date = formatter.dateFromString(self) {
return date
}else{
// if format failed, Put some code here
return nil // an example
}
}
}
Adddivelogviewcontroller.swift
var a = (textFieldStartTime.text.starttimedateValue)
var b = (textFieldEndTime.text.endtimedateValue)
var sum = b - a
textFieldBottomTime.text = "\(sum)"
That's correct-- the - operator is not defined for NSDate?, so you can't find the difference that way. Since a and b are both NSDate?, you could find the difference like this:
if let dateA = a, dateB = b {
let difference = dateA.timeIntervalSinceReferenceDate - dateB.timeIntervalSinceReferenceDate
}
Here, difference's type will be NSTimeInterval?.
Or if you prefer, you could add a definition of - for NSDate?, which might look like this:
func -(lhs:NSDate?, rhs:NSDate?) -> NSTimeInterval? {
if let left=lhs, right=rhs {
return left.timeIntervalSinceReferenceDate - right.timeIntervalSinceReferenceDate
} else {
return nil
}
}
Add that and you can use - above.
OK, not really sure if thats working or not but i get no errors. What I need this to do calculate after the end time picker OK botton is pressed
func OK3ButtonTapped(sender: UIBarButtonItem) {
self.textFieldEndTime.endEditing(true)
self.textFieldEndTime.text = endtimePickerView.date.endtimestringValue
var a = (textFieldStartTime.text.starttimedateValue)
var b = (textFieldEndTime.text.endtimedateValue)
var difference: String = ""
if let dateA = a, dateB = b {
let difference = dateA.timeIntervalSinceReferenceDate - dateB.timeIntervalSinceReferenceDate
}
textFieldBottomTime.text = "\(difference)"
}