Get index of string in an array of NSAttributedString in swift - swift

I have an array of NSattributedstring. I want to get the index of a string in the array. I am using the following code.
let textToSearch = "My name is Amrit"
let index = find(myArray, textToSearch)
myArray is an array of NsattributedString. As both the types of array and textTosearch are different.
indexArray = ["1. THE OPENING","2. THE COW","3. THE FAMILY OF IMRAN","4. WOMEN","5. THE FEAST","6. LIVESTOCK","7. THE HEIGHTS","8. BATTLE GAINS","9. REPENTANCE","10. JONAH","11. HUD","12. JOSEPH","13. THUNDER","14. ABRAHAM","15. AL-HIJR","16. THE BEE","17. THE NIGHT JOURNEY","18. THE CAVE","19. MARY","20. TA HA","21. THE PROPHETS","22. THE PILGRIMAGE","23. THE BELIEVERS","24. LIGHT","25. THE DIFFERENTIATOR","26. THE POETS","27. THE ANTS","28. THE STORY","29. THE SPIDER","30. THE BYZANTINES","31. LUQMAN","32. BOWING DOWN IN WORSHIP","33. THE JOINT FORCES","34. SHEBA","35. THE CREATOR","36. YA SIN","37. RANGED IN ROWS","38. SAD","39. THE THRONGS","40. THE FORGIVER","41. [VERSES] MADE DISTINCT","42. CONSULTATION","43. ORNAMENTS OF GOLD","44. SMOKE","45. KNEELING","46. THE SAND DUNES","47. MUHAMMAD","48. TRIUMPH","49. THE PRIVATE ROOMS","50. QAF","51. SCATTERING [WINDS]","52. THE MOUNTAIN","53. THE STAR","54. THE MOON","55. THE LORD OF MERCY","56. THAT WHICH IS COMING","57. IRON","58. THE DISPUTE","59. THE GATHERING [OF FORCES]","60. WOMEN TESTED","61. SOLID LINES","62. THE DAY OF CONGREGATION","63. THE HYPOCRITES","64. MUTUAL NEGLECT","65. DIVORCE","66. PROHIBITION","67. CONTROL","68. THE PEN","69. THE INEVITABLE HOUR","70. THE WAYS OF ASCENT","71. NOAH","72. THE JINN","73. ENFOLDED","74. WRAPPED IN HIS CLOAK","75. THE RESURRECTION","76. MAN","77. [WINDS] SENT FORTH","78. THEANNOUNCEMENT","79. THE FORCEFUL CHARGERS","80. HE FROWNED","81. SHROUDED IN DARKNESS","82. TORN APART","83. THOSE WHO GIVE SHORT MEASURE","84. RIPPED APART","85. THE TOWERING CONSTELLATIONS","86. THE NIGHT-COMER","87. THE MOST HIGH","88. THE OVERWHELMING EVENT","89. DAYBREAK","90. THE CITY","91. THE SUN","92. THE NIGHT","93. THE MORNING BRIGHTNESS","94. RELIEF","95. THE FIG","96. THE CLINGING FORM","97. THE NIGHT OF GLORY","98. CLEAR EVIDENCE","99. THE EARTHQUAKE","100. THE CHARGING STEEDS","101. THE CRASHING BLOW","102. STRIVING FOR MORE","103. THE DECLINING DAY","104. THE BACKBITER","105. THE ELEPHANT","106. QURAYSH","107. COMMON KINDNESSES","108. ABUNDANCE","109. THE DISBELIEVERS","110. HELP","111. PALM FIBRE","112. PURITY [OF FAITH]","113. DAYBREAK","114. PEOPLE"]
if let rtfPath = NSBundle.mainBundle().URLForResource("quaran3", withExtension: "rtf") {
let attributedStringWithRtf = NSMutableAttributedString(fileURL: rtfPath, options: [NSDocumentTypeDocumentAttribute:NSRTFTextDocumentType], documentAttributes: nil, error: nil)!
var lengthOfRtf = attributedStringWithRtf.length
divideFactor = Int(lengthOfRtf/endIndex)
println(divideFactor)
self.updateTextFont(attributedStringWithRtf) (valueFactor: divideFactor) (totalRange: lengthOfRtf)
self.getTheIndexNumber(attributedStringWithRtf) (valueFactor: divideFactor)
}
func updateTextFont(mystring:NSMutableAttributedString) (valueFactor:Int) (totalRange : Int) {
let screenSizeMain: CGRect = UIScreen.mainScreen().bounds
let myAttriText:NSMutableAttributedString = mystring.mutableCopy() as! NSMutableAttributedString
myAttriText.beginEditing()
myAttriText.enumerateAttributesInRange(NSMakeRange(0, myAttriText.length), options: NSAttributedStringEnumerationOptions.Reverse) { (attribute, range, stop) -> Void in
var mutableAttributes = NSDictionary(dictionary: attribute)
var font:UIFont = mutableAttributes.objectForKey(NSFontAttributeName) as! UIFont
var newFont:UIFont = font.fontWithSize(font.pointSize)
if DeviceType.IS_IPAD
{
newFont = font.fontWithSize(font.pointSize+7)
}
var fontProperties:UIFontDescriptor = font.fontDescriptor()
let sizeNumber:Float = fontProperties.fontAttributes()[UIFontDescriptorSizeAttribute] as! Float
myAttriText.addAttribute(NSFontAttributeName, value: newFont, range: range)
}
for var i=0; i <= valueFactor; i++ {
// if valueFactor*
if startIndex == 885819 {
}
//let checkCurrentRange = valueFactor*endIndex
let exactEndIndex : Int = endIndex
if (totalRange - startIndex) < endIndex
{
endIndex = totalRange - startIndex
let rangeFinal = NSMakeRange(startIndex, endIndex)
var nsTextFinal = myAttriText.attributedSubstringFromRange(rangeFinal)
pageText.append(nsTextFinal)
endIndex = exactEndIndex
return
}
let range = NSMakeRange(startIndex, endIndex)
var nsText = myAttriText.attributedSubstringFromRange(range)
for index in endIndex..<endIndex+100
{
let rangeNew = NSMakeRange(endIndex + startIndex-1,1)
var checkSpace = myAttriText.attributedSubstringFromRange(rangeNew)
let stirngNew = checkSpace.string
if stirngNew == " "
{
break;
}
else
{
endIndex++
}
}
let rangeFinal = NSMakeRange(startIndex, endIndex)
var nsTextFinal = myAttriText.attributedSubstringFromRange(rangeFinal)
pageText.append(nsTextFinal)
println(startIndex)
startIndex = startIndex + endIndex
endIndex = exactEndIndex
}
myAttriText.endEditing()
}
func visibleRangeOfTextView(textView: NSAttributedString) -> NSRange {
return NSMakeRange(startIndex, endIndex)
}
func getTheIndexNumber(attribSring:NSMutableAttributedString) (valueFactor:Int) //-> NSMutableArray
{
for index in 0..<indexArray.count {
let text = indexArray.objectAtIndex(index) as! NSString
var lengthOfRtf = attribSring.length
var textNew = attribSring.attributedSubstringFromRange(NSMakeRange(0, lengthOfRtf))
var stirngNew : NSMutableString = attribSring.mutableString.mutableCopy() as! NSMutableString
// println(stirngNew)
var range: NSRange = (stirngNew as NSString).rangeOfString(text as String)
var checkSpace = attribSring.attributedSubstringFromRange(range)
let stirngNewCheck = checkSpace.string
let pageIndex = (range.location / endIndex)
indeArryInt.addObject(pageIndex)
}
}

You can try this:
let index = myArray.indexOf { $0.string == textToSearch }
This returns an Int?, the first index where a match is found. If there's no match, it will return nil.

If you are using Swift 1.2 you will need to map array of NSAttributedString to String array so find function can be called with correct values
let index = find(myArray.map({ attributedString in
return attributedString.string
}), textToSearch)
#ZoffDino answer would work like a charm in Swift 2.0

Related

Split string by regrex and get matching range

extension String {
func splitWithRegex(by regexStr: String) -> [String] {
guard let regex = try? NSRegularExpression(pattern: regexStr) else { return [] }
let nsRange = NSRange(startIndex..., in: self)
var index = startIndex
var array = regex.matches(in: self, range: nsRange)
.map { match -> String in
let range = Range(match.range, in: self)!
let result = self[index..<range.lowerBound]
index = range.upperBound
return String(result)
}
array.append(String(self[index...]))
return array
}
}
I use the above code to split a string on Swift using a regrex pattern and return an array of strings
How can I return an object of [(String, Range)] so I will use the split substrings as keys and it's range as the value.
For example:
let string = "This is a string"
let stringRange = string.splitWithRegex(by: "\\s+")
Result:
(This, 0...4)
(is, 5...7)
(a, 8...9)
(string, 10...16)
I guess that should do the trick, but I'm not totally sure:
func splitWithRegex2(by regexStr: String) -> [(String, Range<String.Index>)] {
guard let regex = try? NSRegularExpression(pattern: regexStr) else { return [] }
let nsRange = NSRange(startIndex..., in: self)
var index = startIndex
var array = regex.matches(in: self, range: nsRange)
.compactMap { match -> (String, Range<String.Index>)? in
guard let range = Range(match.range, in: self) else { return nil }
let resultRange = index..<range.lowerBound
let string = String(self[resultRange])
index = range.upperBound
guard !resultRange.isEmpty else { return nil } //This should fix the issue where the first match starts your string, in your case, an empty space
let values = (string, resultRange)
return values
}
//Append last value check: needed?
if index < endIndex {
array.append((String(self[index...]), index..<endIndex))
}
return array
}
To play:
let stringReg1 = "This is a string"
let stringRange1 = stringReg1.splitWithRegex2(by: "\\s+")
print(stringRange1)
stringRange1.forEach { //More userfriendly than Range(Swift.String.Index(_rawBits: 65536)..<Swift.String.Index(_rawBits: 327680)))
let nsRange = NSRange($0.1, in: stringReg1)
print("\($0.0), \(nsRange.location)...\(nsRange.location + nsRange.length)")
}
Output:
$>[("This", Range(Swift.String.Index(_rawBits: 1)..<Swift.String.Index(_rawBits: 262144))), ("is", Range(Swift.String.Index(_rawBits: 393216)..<Swift.String.Index(_rawBits: 524288))), ("a", Range(Swift.String.Index(_rawBits: 589824)..<Swift.String.Index(_rawBits: 655360))), ("string", Range(Swift.String.Index(_rawBits: 720896)..<Swift.String.Index(_rawBits: 1114113)))]
$>This, 0...4
$>is, 6...8
$>a, 9...10
$>string, 11...17
let stringReg2 = " This is a string "
let stringRange2 = stringReg2.splitWithRegex2(by: "\\s+")
print(stringRange2)
stringRange2.forEach { //More userfriendly than Range(Swift.String.Index(_rawBits: 65536)..<Swift.String.Index(_rawBits: 327680)))
let nsRange = NSRange($0.1, in: stringReg2)
print("\($0.0), \(nsRange.location)...\(nsRange.location + nsRange.length)")
}
Output:
$>[("This", Range(Swift.String.Index(_rawBits: 262144)..<Swift.String.Index(_rawBits: 524288))), ("is", Range(Swift.String.Index(_rawBits: 655360)..<Swift.String.Index(_rawBits: 786432))), ("a", Range(Swift.String.Index(_rawBits: 851968)..<Swift.String.Index(_rawBits: 917504))), ("string", Range(Swift.String.Index(_rawBits: 983040)..<Swift.String.Index(_rawBits: 1376256)))]
$>This, 4...8
$>is, 10...12
$>a, 13...14
$>string, 15...21

Add # character forward in the every string character in Swift 4

How can I achieve add 3 alphabets on every character of a string?
For example:
Example No. 1: In a certain language ‘CSAT’ is coded as EUCV. How is ‘CIVIL’ written in that language?
Sol: In this, all the letters in a word are moved two places forward.
How can I achieve the same in Swift? What I have tried so far is:
import UIKit
var str: String! = "abc"
let letter = "abcdefghijlklmnopqrstuvwxyz"
var newstring = ""
func getlatestAddedString(arr: [Character]) -> String{
for i in 0..<arr.count{
//Logic goes here
newstring = newstring + String(newchar)
}
return newstring
}
let arr = Array(str)
let newRevStr = getlatestAddedString(arr: arr)
print("Final output is \(newRevStr)")
let startChar = Unicode.Scalar("A").value
let endChar = Unicode.Scalar("Z").value
How can I achieve the same in Swift?
let alphabet = Array("abcdefghijklmnopqrstuvwxyz")
let str = "CSAT"
let codedLetters: [Character] = Array(str).map { letter in
let currentIndex = alphabet.firstIndex(where: {
$0.lowercased() == letter.lowercased()
})!
let newIndex = (currentIndex + 2) % alphabet.count
return alphabet[newIndex]
}
let result = String(codedLetters).uppercased()
print(result) //prints EUCV

How to separate values from an array using Swift 4

How to separate values from an array using Swift 4. Following are my data:
arrWeekly == (
{
date = "2018-04-30";
units = "g/dL";
value = 12;
},
{
date = "2017-06-27";
units = "g/dL";
value = "14.5";
}
)
My Code:
if let arrMonthly = dictPeriod["monthly"] as? [Any], arrMonthly.count > 0
{
self.arrMonth = NSMutableArray(array: arrMonthly)
print("arrMonthly == ",self.arrMonth)
}else{
self.arrMonth = NSMutableArray()
}
I want to separate both dates & Values.
if let arrMonthly = dictPeriod["monthly"] as? [[AnyHasahble:String]], ! arrMonthly.isEmpty {
for disc in arrMonthly{
if let date = disc["date"] as? String{
}
if let units = disc["units"] as? String{
}
if let value = disc["value"] as? String{
}
}
}else{
}
let dictPeriod = YOUR_DICTIONARY
guard let arrMonthly = dictPeriod["monthly"] as? [[String: Any]], !arrMonthly.isEmpty else { return }
let dateArr = arrMonthly.map({ $0["date"] as! String })
let unitsArr = arrMonthly.map({ $0["units"] as! String })
let valueArr = arrMonthly.map({ $0["value"] as! String })

Check the language of numbers if it is English or Arabic/Persian

In my project I am planning to change the language of any type number to Persian and here is what I have done:
public extension String {
func perstianString (string: String)->String {
let digitSet = CharacterSet.decimalDigits
var finalString = String()
for ch in string.unicodeScalars {
if digitSet.contains(ch) {
let sh = convertoPersianNum(num: "\(ch)")
finalString += sh
}
} else {
finalString += "\(ch)"
}
}
return finalString
}
func convertoPersianNum(num: String)->String{
var retVlue = String()
var num1 = num
let number = NSNumber(value: Int(num1)!)
let numb = (num as NSString).intValue
let format = NumberFormatter()
format.locale = Locale(identifier: "fa_IR")
let faNumber = format.string(from: number)
return faNumber!
}
}
But when the source value has Persian numbers, the app crashes. Simply said, I want to check if it is a Persian number, don't do anything, else do the the conversion above:
let string = "ییسس ۱۲۳۴"
with this type do not do anything else do something.
public extension String {
private var isPersian: Bool {
let predicate = NSPredicate(format: "SELF MATCHES %#", "(?s).*\\p{Arabic}.*")
return predicate.evaluate(with: self)
}
private var toPersianNum: String {
let number = NSDecimalNumber(string: self)
let formatter = NumberFormatter()
formatter.locale = Locale(identifier: "fa_IR")
return formatter.string(from: number) ?? ""
}
var persian:String {
var retVal = String()
self.enumerateSubstrings(in: startIndex..<endIndex, options: .byComposedCharacterSequences) { (string, _, _, _) in
guard let string = string else {return}
if string.isPersian {
retVal += string
}else {
retVal += string.toPersianNum
}
}
return retVal
}
}
let string = "9001ییسس2345777 ۱۲۳۴2345"
string.persian // "۹۰۰۱ییسس۲۳۴۵۷۷۷ناعدد۱۲۳۴۲۳۴۵" (result)

drawGlyphsForGlyphRange in Swift (Displaying Invisible characters). replaceGlyphAtIndex deprecated

I am trying to show Invisible characters using below class & func, and it is working fine.
But replaceGlyphAtIndex is deprecated in OS X 10.11 and we need to use setGlyhs()
setGlyphs(<#T##glyphs: UnsafePointer<CGGlyph>##UnsafePointer<CGGlyph>#>, properties: <#T##UnsafePointer<NSGlyphProperty>#>, characterIndexes: <#T##UnsafePointer<Int>#>, font: <#T##NSFont#>, forGlyphRange: <#T##NSRange#>)
But i am having difficulties on how to convert replaceGlyphAtIndex to setGlyphs.
Can any one suggest me how to convert below replaceGlyphAtIndex to setGlyphs()?
I tried as below but it is crashing.
let data = String(g).dataUsingEncoding(NSUTF8StringEncoding)
let cgG = UnsafePointer<CGGlyph>(data!.bytes)
self.setGlyphs(cgG, properties: nil, characterIndexes: UnsafePointer<Int>(bitPattern: characterIndex), font: font as! NSFont, forGlyphRange: NSMakeRange(glyphIndex, 1))
Can any one let me know whats going wrong in the above line of code?
class MyLayoutManager: NSLayoutManager {
override func drawGlyphsForGlyphRange(glyphsToShow: NSRange, atPoint origin: NSPoint) {
if let storage = self.textStorage {
let s = storage.string
let startIndex = s.startIndex
for glyphIndex in glyphsToShow.location ..< glyphsToShow.location + glyphsToShow.length {
let characterIndex = self.characterIndexForGlyphAtIndex(glyphIndex)
let ch = s[startIndex.advancedBy(characterIndex)]
switch ch {
case " ": //Blank Space
let attrs = storage.attributesAtIndex(characterIndex, effectiveRange: nil)
if let font = attrs[NSFontAttributeName] {
let g = font.glyphWithName("period")//("periodcentered")
self.replaceGlyphAtIndex(glyphIndex, withGlyph: g)
// let data = String(g).dataUsingEncoding(NSUTF8StringEncoding)
// let cgG = UnsafePointer<CGGlyph>(data!.bytes)
//
// self.setGlyphs(cgG, properties: nil, characterIndexes: UnsafePointer<Int>(bitPattern: characterIndex), font: font as! NSFont, forGlyphRange: NSMakeRange(glyphIndex, 1))
}
case "\n": //New Line
let attrs = storage.attributesAtIndex(characterIndex, effectiveRange: nil)
if let font = attrs[NSFontAttributeName] {
let g = font.glyphWithName("logicalnot")
self.replaceGlyphAtIndex(glyphIndex, withGlyph: g)
}
case newLineUniCodeStr:
let attrs = storage.attributesAtIndex(characterIndex, effectiveRange: nil)
if let font = attrs[NSFontAttributeName] {
let g = font.glyphWithName("logicalnot")
self.replaceGlyphAtIndex(glyphIndex, withGlyph: g)
}
default:
break
}
}
}
super.drawGlyphsForGlyphRange(glyphsToShow, atPoint: origin)
}
}
I found another way to display Glyphs. (Posting my code below as it may be useful to others)
override func drawGlyphsForGlyphRange(glyphsToShow: NSRange, atPoint origin: NSPoint) {
if let storage = self.textStorage {
let s = storage.string
let startIndex = s.startIndex
var padding:CGFloat = 0
for glyphIndex in glyphsToShow.location ..< glyphsToShow.location + glyphsToShow.length {
let characterIndex = self.characterIndexForGlyphAtIndex(glyphIndex)
if characterIndex < s.characters.count
{
var glyphStr = ""
let ch = s[startIndex.advancedBy(characterIndex)]
switch ch {
case " ": //Blank Space
glyphStr = periodCenteredUniCodeStr
case "\n": //New Line
glyphStr = lineBreakUniCodeStr
case newLineUniCodeStr:
glyphStr = lineBreakUniCodeStr
padding += 5
default:
break
}
var glyphPoint = self.locationForGlyphAtIndex(glyphIndex)
let glyphRect = self.lineFragmentRectForGlyphAtIndex(glyphIndex, effectiveRange: nil)
if glyphStr.characters.count > 0{
glyphPoint.x = glyphPoint.x + glyphRect.origin.x
glyphPoint.y = glyphRect.origin.y
NSString(string: glyphStr).drawInRect(NSMakeRect(glyphPoint.x, glyphPoint.y, 10, 10), withAttributes: nil)
}
}else{
print("Wrong count here")
}
}
}
super.drawGlyphsForGlyphRange(glyphsToShow, atPoint: origin)
}