I want to make a function which compares strings.
I don't want to use equal operators (==), I want it worked only with Swift language.
First I made a function which takes 2 strings, and returns bool type.
then I looped these strings with for in syntax.
And want to compare these characters, if strings have equal value, it should return true, if not, then false. Is there any better way?
func isEqual(str1:String, str2:String) -> Bool {
var result = false
for char in str1 {
}
for char2 in str2 {
}
//Compare characters.
return result
}
== works fine with Strings in Swift. For educational purposes
(as I conclude from your comment "because I'm practicing...")
you can implement it as:
func myStringCompare(str1 : String, str2 : String) -> Bool {
if count(str1) != count(str2) {
return false
}
for (c1, c2) in zip(str1, str2) {
if c1 != c2 {
return false
}
}
return true
}
zip(str1, str2) returns a sequence of pairs from the given
sequences, this is a convenient way to enumerate the strings
"in parallel".
Once you have understood how it works, you can shorten it,
for example to:
func myStringCompare(str1 : String, str2 : String) -> Bool {
return count(str1) == count(str2) && !contains(zip(str1, str2), { $0 != $1 })
}
Comparing the string length is necessary because the zip() sequence
terminates as soon as one of the strings is exhausted. Have a look at
#drewag's answer to In Swift I would like to "join" two sequences in to a sequence of tuples
for an alternative Zip2WithNilPadding sequence.
If you don't want to use the built-in zip() function (again for
educational/self-learning purposes!) then you can use the fact
that Strings are sequences, and enumerate them in parallel using
the sequence generator. This would work not only for strings but
for arbitrary sequences, as long as the underlying elements can
be tested for equality, so let's make it a generic function:
func mySequenceCompare<S : SequenceType where S.Generator.Element : Equatable>(lseq : S, rseq : S) -> Bool {
var lgen = lseq.generate()
var rgen = rseq.generate()
// First elements (or `nil`):
var lnext = lgen.next()
var rnext = rgen.next()
while let lelem = lnext, relem = rnext {
if lelem != relem {
return false
}
// Next elements (or `nil`):
lnext = lgen.next()
rnext = rgen.next()
}
// Are both sequences exhausted?
return lnext == nil && rnext == nil
}
Tests:
mySequenceCompare("xa", "xb") // false
mySequenceCompare("xa", "xa") // true
mySequenceCompare("a", "aa") // false
mySequenceCompare("aa", "a") // false
My solution differ a little as I didn't know about the zip operator, I guess is not as efficient as the one post by Martin great use of tuple.
Great question alphonse
func isEqual(str1:String, str2:String) -> Bool {
if count(str1) != count(str2){
return false
}
for var i = 0; i < count(str1); ++i {
let idx1 = advance(str1.startIndex,i)
let idx2 = advance(str2.startIndex,i)
if str1[idx1] != str2[idx2]{
return false
}
}
return true
}
As pointed by Martin each string needs its own index, as explained by him:
"The "trick" is that "🇩🇪" is an "extended grapheme cluster" and consists of two Unicode code points, but counts as one Swift character."
Link for more details about extended grapheme cluster
Related
So I have a question where I am checking if a string has every letter of the alphabet in it. I was able to check if there is alphabet in the string, but I'm not sure how to check if there is EVERY alphabet in said string. Here's the code
fun isPangram (pangram: Array<String>) : String {
var panString : String
var outcome = ""
for (i in pangram.indices){
panString = pangram[i]
if (panString.matches(".^*[a-z].*".toRegex())){
outcome = outcome.plus('1')
}
else {outcome = outcome.plus('0')}
}
return outcome
}
Any ideas are welcomed Thanks.
I think it would be easier to check if all members of the alphabet range are in each string than to use Regex:
fun isPangram(pangram: Array<String>): String =
pangram.joinToString("") { inputString ->
when {
('a'..'z').all { it in inputString.lowercase() } -> "1"
else -> "0"
}
}
Hi this is how you can make with regular expression
Kotlin Syntax
fun isStrinfContainsAllAlphabeta( input: String) {
return input.lowercase()
.replace("[^a-z]".toRegex(), "")
.replace("(.)(?=.*\\1)".toRegex(), "")
.length == 26;
}
In java:
public static boolean isStrinfContainsAllAlphabeta(String input) {
return input.toLowerCase()
.replace("[^a-z]", "")
.replace("(.)(?=.*\\1)", "")
.length() == 26;
}
the function takes only one string. The first "replaceAll" removes all the non-alphabet characters, The second one removes the duplicated character, then you check how many characters remained.
Just to bounce off Tenfour04's solution, if you write two functions (one for the pangram check, one for processing the array) I feel like you can make it a little more readable, since they're really two separate tasks. (This is partly an excuse to show you some Kotlin tricks!)
val String.isPangram get() = ('a'..'z').all { this.contains(it, ignoreCase = true) }
fun checkPangrams(strings: Array<String>) =
strings.joinToString("") { if (it.isPangram) "1" else "0" }
You could use an extension function instead of an extension property (so it.isPangram()), or just a plain function with a parameter (isPangram(it)), but you can write stuff that almost reads like English, if you want!
if my string is lets say "Alfa1234Beta"
how can I convert all the number in to "_"
for example "Alfa1234Beta"
will be "Alfa____Beta"
Going with the Regex approach pointed out by others is possibly OK for your scenario. Mind you however, that Regex sometimes tend to be overused. A hand rolled approach could be like this:
static string ReplaceDigits(string str)
{
StringBuilder sb = null;
for (int i = 0; i < str.Length; i++)
{
if (Char.IsDigit(str[i]))
{
if (sb == null)
{
// Seen a digit, allocate StringBuilder, copy non-digits we might have skipped over so far.
sb = new StringBuilder();
if (i > 0)
{
sb.Append(str, 0, i);
}
}
// Replace current character (a digit)
sb.Append('_');
}
else
{
if (sb != null)
{
// Seen some digits (being replaced) already. Collect non-digits as well.
sb.Append(str[i]);
}
}
}
if (sb != null)
{
return sb.ToString();
}
return str;
}
It is more light weight than Regex and only allocates when there is actually something to do (replace). So, go ahead use the Regex version if you like. If you figure out during profiling that is too heavy weight, you can use something like the above. YMMV
You can run for loop on the string and then use the following method to replace numbers with _
if (!System.Text.RegularExpressions.Regex.IsMatch(i, "^[0-9]*$"))
Here variable i is the character in the for loop .
You can use this:
var s = "Alfa1234Beta";
var s2 = System.Text.RegularExpressions.Regex.Replace(s, "[0-9]", "_");
s2 now contains "Alfa____Beta".
Explanation: the regex [0-9] matches any digit from 0 to 9 (inclusive). The Regex.Replace then replaces all matched characters with an "_".
EDIT
And if you want it a bit shorter AND also match non-latin digits, use \d as a regex:
var s = "Alfa1234Beta๓"; // ๓ is "Thai digit three"
var s2 = System.Text.RegularExpressions.Regex.Replace(s, #"\d", "_");
s2 now contains "Alfa____Beta_".
So I got stuck on a coding challenge that I almost knew the answer too. And I think I have to use the subString call in Swift 4 to get it 100%. I want to reverse every OTHER word in a string, but ignore or keep the punctuation in its original place( index ).
var sample = "lets start. And not worry about proper sentences."
func reverseString(inputString: String) -> String {
let oldSentence = sample.components(separatedBy: " ")
var newSentence = ""
for index in 0...oldSentence.count - 1 {
let word = oldSentence[index]
if newSentence != "" {
newSentence += " "
}
if index % 2 == 1 {
let reverseWord = String(word.reversed())
newSentence += reverseWord
} else {
newSentence += word
}
}
return newSentence
}
reverseString(inputString: sample)
And this would be the expected output.
"lets trats. And ton worry tuoba proper secnetnes."
Notice the punctuation is not reversed.
You shouldn't use components(separatedBy: ) to split a string in words. See this article for the reason. Use enumerateSubstrings and pass in the appropriate option:
func reverseString(inputString: String) -> String {
var index = 1
var newSentence = inputString
inputString.enumerateSubstrings(in: inputString.startIndex..., options: .byWords) { substr, range, _, stop in
guard let substr = substr else { return }
if index % 2 == 0 {
newSentence = newSentence.replacingCharacters(in: range, with: String(substr.reversed()))
}
index += 1
}
return newSentence
}
print(reverseString(inputString: "lets start. And not worry about proper sentences."))
// lets trats. And ton worry tuoba proper secnetnes.
print(reverseString(inputString: "I think, therefore I'm"))
// I kniht, therefore m'I
I need an implementation of lastIndexOf that is as fast as possible.
I am finding that the String advance function is extremely slow.
I tried using the c function strrchr, and tried copying the string to NSData and using pointers but I can't get the syntax right.
My string will always have 1 byte characters and the string i'm searching for "|" is always 1 byte also.
Any implementation using advance will be too slow but here is the fastest example I could find:
func indexOf(target: String, startIndex: Int) -> Int
{
var startRange = advance(self.startIndex, startIndex)
var range = self.rangeOfString(target, options: NSStringCompareOptions.LiteralSearch, range: Range<String.Index>(start: startRange, end: self.endIndex))
if let range = range {
return distance(self.startIndex, range.startIndex)
} else {
return -1
}
}
func lastIndexOf(target: String) -> Int
{
var index = -1
var stepIndex = self.indexOf(target)
while stepIndex > -1
{
index = stepIndex
if stepIndex + target.length < self.length
{
stepIndex = indexOf(target, startIndex: stepIndex + target.length)
}
else
{
stepIndex = -1
}
}
return index
}
This is an example of the string I need to parse.
var str:String = "4|0|66|5|0|3259744|6352141|1|3259744|WSMxt208L54yZ5irtHC3|Mc02|efland,nc|36.027992|-79.2212834|0|4|6|0|3259744|6352141|46|14|1|0|7|7|3259744|6352141|4|1|0|8|8|3259744|6352141|4|0|22|9|0|3259744|6352141|2|3|Room1|2|72|86330534|1|0|10|9|3259744|6352141|4|1|0|11|10|3259744|6352141|4|1|0|12|11|3259744|6352141|4|1|0|13|12|3259744|6352141|4|0|4|14|0|3259744|6352141|46|24|0|5|15|0|3259744|6352141|46|654|0|66|0|0|3259744|6352141|1|3259744|WSMxt208L54yZ5irtHC3|Mc02|efland,nc|36.027992|-79.2212834|0|4|16|0|3259744|6352141|46|4sageReceived:4|0|66|5|0|3259744|6352141|1|3259744|WSMxt208L54yZ5irtHC3|Mc02|efland,nc|36.027992|-79.2212834|0|4|6|0|3259744|6352141|46|14|1|0|7|7|3259744|6352141|4|1|0|8|8|3259744|6352141|4|0|22|9|0|3259744|6352141|2|3|Room1|2|72|86330534|1|0|10|9|3259744|6352141|4|1|0|11|10|3259744|6352141|4|1|0|12|11|3259744|6352141|4|1|0|13|12|3259744|6352141|4|0|4|14|0|3259744|6352141|46|24|0|5|15|0|3259744|6352141|46|654|0|66|0|0|3259744|6352141|1|3259744|WSMxt208L54yZ5irtHC3|Mc02|efland,nc|36.027992|-79.2212834|0|4|16|0|3259744|6352141|46|4352141|1|3259744|WSMxt208L54yZ5irtHC3|Mc02|efland,nc|36.027992|-79.2212834|0|4|6|0|3259744|6352141|46|14|1|0|7|7|3259744|6352141|4|1|0|8|8|3259744|6352141|4|0|22|9|0|3259744|6352141|2|3|Room1|2|72|86330534|1|0|10|9|3259744|6352141|4|1|0|11|10|3259744|6352141|4|1|0|12|11|3259744|6352141|4|1|0|13|12|3259744|6352141|4|0|4|14|0|3259744|6352141|46|24|0|5|15|0|3259744|6352141|46|654|0|66|0|0|3259744|6352141|1|3259744|WSMxt208L54yZ5irtHC3|Mc02|efland,nc|36.027992|-79.2212834|0|4|16|0|3259744|6352141|46|4TCPListener.onReceived: 4|0|66|5|0|3259744|6352141|1|3259744|WSMxt208L54yZ5irtHC3|Mc02|efland,nc|36.027992|-79.2212834|0|4|6|0|3259744|6352141|46|14|1|0|7|7|3259744|6352141|4|1|0|8|8|3259744|6352141|4|0|22|9|0|3259744|6352141|2|3|Room1|2|72|86330534|1|0|10|9|3259744|6352141|4|1|0|11|10|3259744|6352141|4|1|0|12|11|3259744|6352141|4|1|0|13|12|3259744|6352141|4|0|4|14|0|3259744|6352141|46|24|0|5|15|0|3259744|6352141|46|654|0|66|0|0|3259744|6352141|1|3259744|WSMxt208L54yZ5irtHC3|Mc02|efland,nc|36.027992|-79.2212834|0|4|16|0|3259744|6352141|46|4preParse
4|0|66|5|0|3259744|6352141|1|3259744|WSMxt208L54yZ5irtHC3|Mc02|efland,nc|36.027992|-79.221283"
Here is a Swift 2.0 Answer
func lastIndexOf(s: String) -> Int? {
if let r: Range<Index> = self.rangeOfString(s, options: .BackwardsSearch) {
return self.startIndex.distanceTo(r.startIndex)
}
return Optional<Int>()
}
Tests
func testStringLastIndexOf() {
let lastIndex = "0|2|45|7|9".lastIndexOf("|")
XCTAssertEqual(lastIndex, 8)
}
func testStringLastIndexOfNotFound() {
let lastIndex = "0123456789".lastIndexOf("|")
XCTAssertEqual(lastIndex, nil);
}
You can use strrchr in Swift
import Darwin
let str = "4|0|66|5|0|3259744|6352141|1|3259744"
func stringLastIndexOf(src:String, target:UnicodeScalar) -> Int? {
let c = Int32(bitPattern: target.value)
return src.withCString { s -> Int? in
let pos = strrchr(s, c)
return pos != nil ? pos - s : nil
}
}
stringLastIndexOf(str, "|") // -> {Some 28}
stringLastIndexOf(str, ",") // -> nil
You can use Objective C files in a Swift project; in these you can use plain C code and make a function which uses strrchr. Then you can call this from Swift.
If you do this in order to get all substring delimited by "|", you might test this approach:
import Foundation
let s = "4|0|66|5|0|3259744|6352141|1|3259744|WSMxt208L54yZ5irtHC3|..."
let a = s.componentsSeparatedByString("|")
The built in functions are sometimes very fast and you may be getting the required performance even by using String.
If you really need to get only the position of the last "|", you could work with utf16 representation, where advancing over the characters should be faster.
I think this should work:
let utf16String = s.utf16
var i = s.utf16Count - 1
while i >= 0 {
if utf16String[i] == 124 {
break
}
i--
}
println(i)
If the characters are guaranteed as single byte, the data is huge and performance is critical then it may be worth converting to an array of bytes (UInt8) and perform the operations directly on them. You can then convert the part that you need back to a String.
Also note that Optimised builds may be much faster than Debug builds so you should do any performance testing with the optimiser on. It may also be worth checking that the optimised versions are too slow at the moment.
Looking for either a solution, some ideas or being point in the right direction on how to resolve a problem.
Basically, I have to figure out if a string value is in between a Low and High string value. However, the values are in a format which String.Compare will not work. But, a human can easily figure out.
For example, one of my ranges is Low: A7, High A12. A8 fits in between those values but String.Compare says it does not. A13 would not fit between the values.
Other examples of Low and High values are:
Low Value - High Value
1A1 - 1A12
25W00 - 25W050
42W1 - 42W296
W232N0002 - W232N000598
In the above examples 1A2 would fit between the Low High Value of 1A1 and 1A12, but 1A100 would not.
Any ideas on how to resolve this? I know this had to have been encountered before.
This could use some optimization, but it's a proof of concept.
Just convert the letters to numerical values and compare the results:
private bool ValueIsBetween(string value, string lowValue, string highValue)
{
long low = long.Parse(ConvertToNumber(lowValue));
long high = long.Parse(ConvertToNumber(highValue));
long val = long.Parse(ConvertToNumber(value));
return val > low && val < high;
}
private string ConvertToNumber(string value)
{
value = value.ToUpper();
value = value.Replace("A", "0");
value = value.Replace("B", "1");
value = value.Replace("C", "2");
value = value.Replace("D", "3");
value = value.Replace("E", "4");
value = value.Replace("F", "5");
value = value.Replace("G", "6");
value = value.Replace("H", "7");
value = value.Replace("I", "8");
value = value.Replace("J", "9");
value = value.Replace("K", "10");
value = value.Replace("L", "11");
value = value.Replace("M", "12");
value = value.Replace("N", "13");
value = value.Replace("O", "14");
value = value.Replace("P", "15");
value = value.Replace("Q", "16");
value = value.Replace("R", "17");
value = value.Replace("S", "18");
value = value.Replace("T", "19");
value = value.Replace("U", "20");
value = value.Replace("V", "21");
value = value.Replace("W", "22");
value = value.Replace("X", "23");
value = value.Replace("Y", "24");
value = value.Replace("Z", "25");
return value;
}
Results:
ValueIsBetween("1A2", "1A1", "1A12");
true
ValueIsBetween("1A100", "1A1", "1A12");
false
ValueIsBetween("43W4", "42W1", "44W3");
true
Edit:
Try this improved algorithm instead:
private bool ValueIsBetween(string value, string lowValue, string highValue)
{
return !ValueIsLessThan(value, lowValue) && ValueIsLessThan(value, highValue);
}
private bool ValueIsLessThan(string value, string compareTo)
{
var matches = Regex.Matches(value, "[0-9]+|[a-zA-Z]+");
var matchesB = Regex.Matches(compareTo, "[0-9]+|[a-zA-Z]+");
var count = matches.Count < matchesB.Count ? matches.Count : matchesB.Count;
for (int i = 0; i < count; i++)
{
long val;
long val2;
if (long.TryParse(matches[i].Value, out val))
{
if (long.TryParse(matchesB[i].Value, out val2))
{
if (val > val2) return false;
if (val < val2) return true;
}
else
{
return false;
}
}
else
{
if (matches[i].Value.CompareTo(matchesB[i].Value) > 0 ) return false;
if (matches[i].Value.CompareTo(matchesB[i].Value) < 0 ) return true;
}
}
return true;
}
Results:
ValueIsBetween("B431Z543", "A0", "Z9");
true
ValueIsBetween("4B31Z543", "A0", "Z9");
false
ValueIsBetween("1A2", "1A1", "1A12");
true
ValueIsBetween("1A100", "1A1", "1A12");
false
ValueIsBetween("43W4", "42W1", "44W3");
true
ValueIsBetween("W5", "CC4", "CC6");
false
ValueIsBetween("W8B4", "W5C3", "W7C3");
false
ValueIsBetween("W5C4", "W5C3", "C7W3");
false
Build a class, probably abstract, with sub classes for each pattern.
The pattern for "25W00" could be ^(?<LEFTTHING>.{2})(?<MIDDLETHING>.{1})(?<RIGHTTHING>.{2})$
In your class, capture each Regex group as a string or numeric as appropriate.
I suppose you could come up with some conventions so you might even be able to have a single Type - and pass in that pattern to the constructor.
You may even have some kind of really smart class where you pass in two strings and a common pattern. This super class builds appropriate "comparable" classes (as per above) and returns a boolean result of the comparison. Your client code would be very clean in this case.
Assuming the non-numeric parts are fixed (i.e. you aren't searching for 1B1 being between 1A1 and 1C1), you could use a regex to expand the numerical values to a certain fixed width, so you could then compare the strings.
For example, using
static Regex digits = new Regex(#"\d+");
static string ExpandDigits(string s)
{
return digits.Replace(s, m => string.Format("{0:D10}", int.Parse(m.ToString())));
}
then calling ExpandDigits("W232N0002") yields W0000000232N0000000002.
You could have a comparison method like this:
static bool IsInRange(string lower, string upper, string test)
{
test = ExpandDigits(test);
lower = ExpandDigits(lower);
if (lower.CompareTo(test) <= 0)
{
upper = ExpandDigits(upper);
if (test.CompareTo(upper) <= 0)
{
return true;
}
}
return false;
}