First character of every word while retaining punctuation

I want to create a service where I highlight text and it extracts the first character of every word while retaining punctuation.

For example, I highlight “Here’s to the crazy ones. The misfits. The rebels. The troublemakers. The round pegs in the square holes. The ones who see things differently. They’re not fond of rules. And they have no respect for the status quo. You can quote them, disagree with them, glorify or vilify them.” and it becomes “H t t c o. T m. T r. T t. T r p i t s h. T o w s t d. T n f o r. A t h n r f t s q. Y c q t, d w t, g o v t.”

In the above example it should retain the commas and periods.

I’m new and my current script is very bad. It only handles one sentence.

set theText to "Here's to the crazy ones."
set theList to theText's text items
set thePunct to (reverse of theList)
set theNewPunct to (item 1 of thePunct)
set theList to every word of theText
set temp to ""
repeat with theWord in theList
	set theLetter to (first character of theWord)
	set temp to temp & theLetter & " "
end repeat
set theNewText to characters 1 thru -2 of temp as string
set theNewText to theNewText & theNewPunct

I’m willing to learn. Thank you.

Hello! :slight_smile:

It was a fun diversion! And I want credit in any doc for your service, should you ever share it! :smiley:

set AppleScript's text item delimiters to ""
set shortenedText to {}
set thetext to "Here's to the crazy ones. The misfits. The rebels. The troublemakers. The round pegs in the square holes. The ones who see things differently. They're not fond of rules. And they have no respect for the status quo. You can quote them, disagree with them, glorify or vilify them."

set thePars to every paragraph of thetext

repeat with aPar in thePars
    
    set end of shortenedText to everyPeriod from aPar
    
end repeat


-- Result: "H t t c o. T m. T r. T t. T r p i t s h. T o w s t d. T n f o r. A t h n r f t s q. Y c q t, d w t, g o v t.""H t t c o. T m. T r. T t. T r p i t s h. T o w s t d. T n f o r. A t h n r f t s q. Y c q t, d w t, g o v t."

to everyPeriod from aParagraph
    script s
        property tPc : " "
    end script
    
    iqueue()
    set tt to {}
    set res to {}
    
    set rParagraph to reverse of every character of aParagraph as text
    set ct to count rParagraph
    set ofa to offset of "." in rParagraph
    set ofb to offset of "!" in rParagraph
    set ofc to offset of "?" in rParagraph
    set ofs to min3({ofa, ofb, ofc}, {".", "!", "?"}, a reference to s's tPc)
    fullPerQadd(contents of s's tPc)
    set end of tt to {(ct - ofs + 1)}
    set factor to 0
    repeat
        set ofa to offset of "." in (characters (ofs + 1) through -1 of rParagraph as text)
        set ofb to offset of "!" in (characters (ofs + 1) through -1 of rParagraph as text)
        set ofc to offset of "?" in (characters (ofs + 1) through -1 of rParagraph as text)
        set ofs to min3({ofa, ofb, ofc}, {".", "!", "?"}, a reference to s's tPc)
        
        if ofs is 0 then exit repeat
        fullPerQadd(contents of s's tPc)
        set factor to factor + ofs + 1
        set ofs to factor
        set end of tt to {ct - factor + 2}
    end repeat
    set end of tt to {0}
    set tt to reverse of tt
    set fullPeriods to {}
    repeat with i from 1 to ((get count tt) - 1)
        
        set afullPer to characters (((item i of tt) + 1) as number) thru ((item ((i + 1)) of tt) as number) of aParagraph as text
        copy afullPer to end of fullPeriods
    end repeat
    
    
    repeat with afullPer in fullPeriods
        set ofs to 0
        set factor to 0
        set tt to {}
        set ct to (get count afullPer)
        set rfullPer to reverse of every character of afullPer as text
        repeat
            set ofa to offset of "," in (characters (ofs + 1) through -1 of rfullPer as text)
            set ofb to offset of ";" in (characters (ofs + 1) through -1 of rfullPer as text)
            set ofc to offset of ":" in (characters (ofs + 1) through -1 of rfullPer as text)
            set ofs to min3({ofa, ofb, ofc}, {",", ";", ":"}, a reference to s's tPc)
            
            if ofs is 0 then exit repeat
            perQueueAdd(contents of s's tPc)
            set factor to factor + ofs + 1
            set ofs to factor
            set end of tt to {ct - factor + 2}
        end repeat
        if tt is not {} then
            set tt to {ct} & tt
            set end of tt to {0}
            set tt to reverse of tt
            set Periods to {}
            
            repeat with i from 1 to ((get count tt) - 1)
                
                set per to characters (((item i of tt) + 1) as number) thru ((item ((i + 1)) of tt) as number) of afullPer as text
                copy per to end of Periods
            end repeat
            set np to (get count Periods)
            repeat with j from 1 to np
                set nw to (get count every word of item j of Periods)
                
                repeat with k from 1 to nw
                    if k is less than nw then
                        copy character 1 of word k of item j of Periods to end of res
                    else if j is less than np then
                        copy ((character 1 of word k of item j of Periods) & popPerQueue()) to end of res
                    else
                        copy ((character 1 of word k of item j of Periods) & popFullPerQueue()) to end of res
                    end if
                end repeat
            end repeat
        else -- no periods
            set wl to every word of afullPer
            set nf to (get count wl)
            repeat with l from 1 to nf
                if l is less than nf then
                    copy character 1 of word l of afullPer to end of res
                else
                    copy ((character 1 of word l of afullPer) & popFullPerQueue()) to end of res
                end if
            end repeat
        end if
    end repeat
    
    set {tids, AppleScript's text item delimiters} to {AppleScript's text item delimiters, " "}
    set res to res as text
    set AppleScript's text item delimiters to tids
    
    
    log res
    return res
end everyPeriod
on fullPerQadd(athing)
    global fullPerqueue
    
    set fullPerqueue to {athing} & fullPerqueue
    return contents of item 1 of fullPerqueue
    
end fullPerQadd

on perQueueAdd(athing)
    global perQueue
    
    set perQueue to {athing} & perQueue
    return contents of item 1 of perQueue
end perQueueAdd

on popPerQueue()
    global perQueue
    try
        copy (contents of (first item of perQueue)) to queueTop
    on error
        error "The queue is empty!!!" number 3000
    end try
    try
        set perQueue to rest of perQueue
    end try
    return queueTop
end popPerQueue

on popFullPerQueue()
    global fullPerqueue
    try
        copy (contents of (first item of fullPerqueue)) to queueTop
    on error
        error "The queue is empty!!!" number 3000
    end try
    try
        set fullPerqueue to rest of fullPerqueue
    end try
    return queueTop
end popFullPerQueue

on iqueue()
    global perQueue, fullPerqueue
    set {perQueue, fullPerqueue} to {{}, {}}
end iqueue

on min(a, b)
    if a is less than b then
        return a
    else
        return b
    end if
end min


on min3(l, m, n)
    set {a, b, c} to {item 1 of l, item 2 of l, item 3 of l}
    set {oka, okb, okc} to {false, false, false}
    if a is greater than 0 then set oka to true
    if b is greater than 0 then set okb to true
    if c is greater than 0 then set okc to true
    
    if oka and okb and okc then
        set d to min(a, b)
        set e to min(b, c)
        
        set f to min(d, e)
        
        if f is a then
            set contents of n to contents of item 1 of m
        else if f is b then
            set contents of n to contents of item 2 of m
        else
            set contents of n to contents of item 3 of m
        end if
        return f
    else if oka and okb then
        set d to min(a, b)
        if d is a then
            set contents of n to contents of item 1 of m
        else
            set contents of n to contents of item 2 of m
        end if
        return d
    else if oka and c then
        set d to min(a, c)
        if d is a then
            set contents of n to contents of item 1 of m
        else
            set contents of n to contents of item 3 of m
        end if
        return d
    else if okb and okc then
        set d to min(b, c)
        if d is b then
            set contents of n to contents of item 2 of m
        else
            set contents of n to contents of item 3 of m
        end if
        return d
        return min(b, c)
    else if oka then
        set contents of n to contents of item 1 of m
        return a
    else if okb then
        set contents of n to contents of item 2 of m
        return b
    else if okc then
        set contents of n to contents of item 3 of m
        return c
    else
        return 0
    end if
end min3

Result output:

"H t t c o. T m. T r. T t. T r p i t s h. T o w s t d. T n f o r. A t h n r f t s q. Y c q t, d w t, g o v t."

Yep. It was a toughie! I’ve ignored the possibility of quotes here:

set theText to "Here's to the crazy ones. The misfits. The rebels. The troublemakers. The round pegs in the square holes. The ones who see things differently. They're not fond of rules. And they have no respect for the status quo. You can quote them, disagree with them, glorify or vilify them."

do shell script "echo " & quoted form of theText & " | sed -E \"s/((^| )[A-Za-z0-9]|[.,:;?!])([A-Za-z0-9'']*)/\\1/g\""
--> "H t t c o. T m. T r. T t. T r p i t s h. T o w s t d. T n f o r. A t h n r f t s q. Y c q t, d w t, g o v t."

No credits needed for this example :stuck_out_tongue:

set thetext to "Here's to the crazy ones. The misfits. The rebels. The troublemakers. The round pegs in the square holes. The ones who see things differently. They're not fond of rules. And they have no respect for the status quo. You can quote them, disagree with them, glorify or vilify them."
set thetext to stringReplace(character id 9, character id 32, thetext)
set thetext to stringReplace(character id 10, character id 32, thetext)
set thetext to stringReplace(character id 13, character id 32, thetext)

set theTextItems to stringExplode(thetext, character id 32)
set tmp to {}
repeat with w in theTextItems
	set s to character 1 of w
	if last character of w is in {".", "?", "!", ","} then set s to s & last character of w
	set end of tmp to s
end repeat
return stringImplode(tmp, character id 32)

on stringReplace(s1, s2, str)
	set AppleScript's text item delimiters to s1
	set l to every text item of str
	set AppleScript's text item delimiters to s2
	set s to l as string
	set AppleScript's text item delimiters to ""
	return s
end stringReplace

on stringExplode(str, d)
	set AppleScript's text item delimiters to d
	set l to every text item of str
	set AppleScript's text item delimiters to ""
	return l
end stringExplode

on stringImplode(l, d)
	set AppleScript's text item delimiters to d
	set s to l as string
	set AppleScript's text item delimiters to ""
	return s
end stringImplode

Hello!

Your solution is much neater! But then I wanted a starting point for parsing paragraphs into full periods and periods, which it doesn’t do totally correctly by now, but maybe in due time…

Hello!

I have updated my solution, my very slow solution, but still a solution that works, and that I can use for putting in a couple of hard spaces after periods should I ever wish to do so, to make text look better in browsers.

I know, a regexp can do that quite easily, but this was more fun! :smiley:

It is now uglier than ever, but considers “.”,“!” and “?” as markers for a full period, and “,”,“;” and “:” as markers for a period.

Isn’t this all it takes? This takes into account all the punctuation that has been mentioned.

set theSelection to "Here's  to \"the crazy\" ones. \"The misfits\". The rebels. The troublemakers. The round pegs in the square holes. The ones who see things differently. They're not fond of rules. And they have no respect for the status quo. You can quote them, disagree with them, glorify or vilify them."

set punctuationToKeep to {",", ".", "!", "?", ";", ":", "\""}
set charList to characters of theSelection

set stringList to {}
set foundSpace to true
repeat with i from 1 to count of charList
	set aChar to item i of charList
	if foundSpace and aChar is not in punctuationToKeep and aChar is not space then
		set end of stringList to aChar
		set foundSpace to false
	else if aChar is space then
		set end of stringList to space
		set foundSpace to true
	else if aChar is in punctuationToKeep then
		set end of stringList to aChar
	end if
end repeat

return stringList as text

Performance wise I would think using text item delimiters would be faster but it seems that both our scripts are equally fast. Just another great example that shows that in AS there are many different ways to achieve the same goal without one beeing better than the other. AS keeps surprising me because in in many many language an text item delimiter way would be almost in any way the worse/cheap solution. Working stream-wise would always be better.

Hello!

I have often felt that splitting text into paragraphs is to coarse, so I made a starting point for getting periods and full periods out of paragraphs of text.

So my solution to the problem is an overkill, but it will hack what I opted for.