Close-Knit Words

Introduction

According to Rand Al'Thor's post in the Puzzling SE, a close-knit word is any word that contains three alphabetically consecutive letters (in any order).

Words like education, foghorn and cabaret are all considered close-knit words whereas words like learning, klaxon and perform are not close-knit words.

Challenge

The challenge is to code-golf a program capable of taking a single word as input (assumed lower case, for all intents and purposes) and to return output that (if available) lists all consecutive letter sets (also in lower case) if it is a close-knit word, and empty output if it is not a close-knit word.

Examples

Input: education
Output: cde

Input: foghorn
Output: fgh

Input: cabaret
Output: abc

Input: hijacking
Output: ghi, hij, ijk

Input: pneumonia
Output: mno, nop

Input: klaxon
Output: <<no output>>

Input: perform
Output: <<no output>>

Input: learning
Output: <<no output>>


Rules

1. Whereas input is to be assumed to be a single lower-case word and output must be lower-case, the nature of the output will vary according to the choice of your coding language. Please select a form of output that will best suit the nature of the challenge, whether it be STDOUT, file output, array, etc.
2. Because this is code-golf, it will be a case of lowest number of bytes being the clear winner.
3. No silly loopholes.
4. I will not accept answers that have the consecutive letters in non-alphabetical order... So cab will not be deemed a suitable output for cabaret, for example.
5. Special note, while the "triplets" don't necessarily have to be in alphabetical order, but the characters within the triplets must be... so in the case of the word "performance", for example, the output mno,nop will be accepted, as will nop,mno. In the case of the word "hijacking", there are six ways that the triplets of ghi, hij and ijk could be arranged in a list, and all six permutations are acceptable as output.

Other than that, on your marks, get set, golf!

• Can the output be a 2D char array with each set of three consecutive letters in a column? – Luis Mendo Sep 19 '16 at 23:11
• @LuisMendo Can you give me an example just so that I can visualize it? – WallyWest Sep 19 '16 at 23:28
• Try my code without the final ! And with another word, as the current one gives the same result :-) – Luis Mendo Sep 19 '16 at 23:30
• @LuisMendo is MATL column major or something? – Maltysen Sep 19 '16 at 23:37
• Is an output in the format of an array of tuples okay, i.e. the output for pneumonia can be [('m','n','o'),('n','o','p')])? – R. Kap Sep 20 '16 at 23:44

05AB1E, 76 5 bytes

Code:

3ãAŒÃ


Explanation:

3ã      # Cartesian product × 3 with input
AŒ    # All substrings of the alphabet
Ã   # Setwise intersection


Uses the CP-1252 encoding. Try it online!

• This is just sheer genius... I'll have to try creating a challenge that pushes this language to the limit... ;) – WallyWest Sep 21 '16 at 0:13

Python 3.5, 68 bytes

w=input()
a=0
while 1:s='%c'*3%(a,a+1,a+2);a+=1;{*s}-{*w}or print(s)


Prints output strings, and terminates with error when the character value gets too large.

Generates all strings of three consecutive and prints those that are a subset of the input word.

Pyth - 11108 7 bytes

Super brute force method.

@^z3.:G

@            Setwise intersection, finds common strings between the two lists
^           Cartesian product
z          Input
3          Of length 3
.:          Substrings. Without second input finds all substrings which is ok
G          Lowercase aphabet


Jelly, 7 bytes

ØaẆfṗ3$ This is a monadic link. Try it online! How it works ØaẆfṗ3$  Monadic link. Argument: s (string)

Øa       Yield the lowercase alphabet, i.e., a := "abcdefghijklmnopqrstuvwxyz".
Ẇ      Window; yields all substrings of a.
$Combine the two links to the left into a monadic chain. ṗ3 Take the third Cartesian power of s, yielding all combinations of three characters that can be formed from the letters in s. f Filter; keep only those substrings of a that appear in the Cart. power.  JavaScript (ES6), 95 90 bytes f= s=>[...s].map(c=>a[parseInt(c,36)]=c,a=[])&&a.map((c,i)=>c+a[i+1]+a[i+2]).filter(c=>!c[3]) ; <input oninput="o.textContent=f(this.value).join, "><div id=o> Missing values concatenate as undefined, so the resulting string contains greater than 3 characters. I borrowed the !c[3] trick from @ETHproductions to save 5 bytes. • Coincidentally enough undefined is a close-knit word ;) – WallyWest Sep 19 '16 at 23:43 • Why parseInt(c,36) instead of c.charCodeAt()? – Titus Sep 20 '16 at 0:51 • @Titus I guess it makes no difference, I'm just used to using parseInt in code golf. – Neil Sep 20 '16 at 7:56 Python 3.5, 78 bytes s={*input()} for c in s:o=ord(c);a=chr(o-2);b=chr(o-1);s>{a,b}and print(a+b+c)  PHP, 100 bytes for($a="abc",$i=98;$i<123;$a=substr($a,1).chr(++$i))if(strstr(count_chars($argv[1],3),$a))echo"$a,";


takes input as command line argument; prints a trailing comma. run with -r.

• for($s=join(range(Z,z));$a=substr($s,++$i,3);) is a shorter way of creating $a. It admittedly checks a bunch of punctuation and also some 2-character series but as the input is lower case letters only and it requires that it finds 3 characters that's fine. – user59178 Sep 20 '16 at 13:33 • for($a="ab",$i=98;$i<123;)!strstr(count_chars($argv[1],3),$a=substr($a,1).chr(++$i))?:print"$a,"; saves 2 Bytes. Very nice way, I have tried other ways in PHP but can't reach the Bytes of your code. I'm not sure if you need a space after the comma – Jörg Hülsermann Sep 20 '16 at 17:02 C, 175 174 bytes main(_,a,m,n)char**a;{char*s=a[1],*t=s;while(*++s)while(s>t&&(n=s[-1])>=*s){m=(*s^n)?*s:0;*s=n;*--s=m;!*t&&++t;}for(;t[1]&t[2];++t)*t==t[1]-1&&*t==t[2]-2&&printf("%.3s ",t);}  indented: main(_,a,m,n)char**a; { char*s=a[1],*t=s; while(*++s) while(s>t&&(n=s[-1])>=*s){ m=(*s^n)?*s:0; *s=n; *--s=m; !*t&&++t; } for(;t[1]&t[2];++t) *t==t[1]-1&&*t==t[2]-2&&printf("%.3s ",t); }  While doing the sort it replaces duplicate values with 0s, these 0s get sorted to the beginning of the word. Looking for the consecutive values is then trivial. • Welcome to the site! – DJMcMayhem Sep 21 '16 at 17:28 • Yes, welcome to PPCG! Great work on your answer, C is not one of the easiest to golf in! – WallyWest Sep 21 '16 at 20:20 MATL, 13 bytes 2Y23YCtjmAZ)!  Try it online! 2Y2 % Push string of lowercase alphabet 3YC % 2D char array with sliding blocks of size 3, each on a column t % Duplicate j % Take input m % Member function: true for elements of the 2D array that are in the input A % All: true for columns that consist of all true values Z) % Use as logical index into the columns of the 2D char array ! % Transpose. Implicitly display  Haskell, 48 bytes f w=filter(all(elemw))[take 3[c..]|c<-['a'..]]  Generates all triples of three consecutive characters, takes those that use only letters in the input. 53 bytes: f w=filter(all(elemw))[[pred$pred c..c]|c<-['c'..]]


The list ['c'..] contains all unicode characters from 'c' onward. The list comprehension [[pred$pred c..c]|c<-['c'..]] turns them into all strings of 3 consecutive characters from "abc" onward. We step backwards with [pred$pred c..c] instead of forwards with [c..succ$succ c] to avoid an error when taking the successor of the highest unicode character. These triples are filtered for those that only use letters in the input. Perl, 36 bytes Includes +1 for -n Give input on STDIN: perl -nE 'join("",a..z)=~/[$_]{3}(?{say$&})^/' <<< "hijacking"  Just the code: join("",a..z)=~/[$_]{3}(?{say$&})^/  T-SQL, 153 bytes Had to react on the comment from WallyWest, about it being long time since last TSQL answer. Answer was partly inspired by Brian J's answer Golfed: USE MASTER DECLARE @ varchar(max)='hijacking' ;WITH C as(SELECT distinct ascii(substring(@,number,1))z FROM spt_values)SELECT CHAR(C.z)+CHAR(D.z)+CHAR(E.z)FROM C,C D,C E WHERE c.z+1=d.z and d.z=e.z-1  Fiddle Ungolfed: USE MASTER -- can be left out if the master database is already being used DECLARE @ varchar(max)='hijacking' ;WITH C as ( SELECT distinct ascii(substring(@,number,1))z FROM spt_values ) SELECT CHAR(C.z)+CHAR(D.z)+CHAR(E.z) FROM C,C D,C E WHERE c.z+1=d.z and d.z=e.z-1  • That's clever! Didn't even know about that table. Good thing there aren't words more than 2048 letters long! – Brian J Sep 21 '16 at 14:41 Haskell, 6360 52 bytes f w=[x|x<-take 3<$>scanr(:)"_"['a'..],all(elemw)x]


Usage example: f "hijacking" -> ["ghi","hij","ijk"].

scanr(:)"_"['a'..] builds a list with the tails of the list of all unicode chars starting with 'a' and lets it end with a '_', i.e. ["abcde...\1114111_", "bcde...\1114111_", "cde...\1114111_", ..., "\1114109\1114110\1114111_", "\1114110\1114111_", "\1114111_", "_"]. Then we take up to three chars of each string and bind it to x. Keep all x where every letter of it is in the input parameter w.

Edit: @xnor saved 3 7 bytes. Thanks!

• Does anyone know if I can capture the first three elements of the list a:b:c:_ with an @-pattern? – nimi Sep 19 '16 at 23:09
• I don't know about the @-pattern, but you can remove the 'z' upper bound and just let it try all characters. – xnor Sep 19 '16 at 23:13
• Capturing those first 3 elements is really annoying. Best I can see is just to use take and remove the empty string: f w=[x|x<-init$take 3<$>scanr(:)""['a'..],all(elemw)x] – xnor Sep 19 '16 at 23:38
• @xnor: nice. we can start the scanr with "." instead of "" and omit the init$. – nimi Sep 19 '16 at 23:49 T-SQL (SQL Server 2014), 217 bytes Golfed declare @ table(a char)declare @i int=1while @i<=len(@a)begin insert into @ values(SUBSTRING(@a,@i,1))set @i+=1 end select distinct t.a+u.a+v.a from @ t,@ u,@ v where ASCII(t.a)+1=ASCII(u.a)and ASCII(u.a)+1=ASCII(v.a)  Usage First declare the variable @a as a char of some sort and assign the input like so declare @a varchar(max) = 'pneumoultramicroscopicsilicovolcanoconiosis'  I didn't count the declare as part of my code, but I didn't find a sql standard for input, so I'm willing to change my counting Output will either be one row for each triple, or no rows if the word is not close knit Ungolfed declare @temp table ( letter char(1) ) -- table to hold each letter of the word declare @i int = 1 while @i <= len(@input) -- split each letter, and each row in @temp will have one letter begin insert into @temp values (SUBSTRING(@input, @i, 1)) set @i = @i + 1 end -- join the letters table to itself to get three letter triples, where the three are in adjacent increasing order -- use distinct because there might be duplicates in the word select distinct t1.letter + t2.letter + t3.letter from @temp t1 cross apply @temp t2 cross apply @temp t3 where ASCII(t1.letter) + 1 = ASCII(t2.letter) and ASCII(t2.letter) + 1 = ASCII(t3.letter)  • the declaration wouldn't be counted seeing we're dealing with the code that would be required to execute the required functionality post-declaration in this instance. Great work, been a while since I've seen an SQL solution for a challenge. Great work! – WallyWest Sep 20 '16 at 23:33 • I have golfed your script down to 185 characters, here is the ungolfed version. You may want to check out my answer as well – t-clausen.dk Sep 21 '16 at 11:04 R, 220 bytes My solution is pretty straightforward. It loops through the possible three letter combinations, loops through and checks the characters of the entered string against the three consecutive letters, and adds them to a string. The string is then only printed when three letters are found (c==4). f<-function(w){if(nchar(w)>2){for(i in 1:24){ c<-1 t<-"" for(k in 1:3){for(j in 1:nchar(w)){if(substr(w,j,j)==intToUtf8(95+k+i)&c<4){ t<-paste(t,substr(w,j,j),sep="") c<-c+1 break }}} if(c==4){print(paste(t))}}}}  input/output > f("education") > [1] "cde" > > f("foghorn") > [1] "fgh" > > f("cabaret") > [1] "abc" > > f("hijacking") > [1] "ghi" > [1] "hij" > [1] "ijk" > > f("pneumonia") > [1] "mno" > [1] "nop" > > f("klaxon") > > f("perform") > > f("learning") > >  Python 3.5, 1141118880 79 bytes: lambda X,W=[*map(chr,range(65,91))]:[i*({*X}>={*i})for i in zip(W,W[1:],W[2:])]  An anonymous lambda function. Takes input as an uppercase string and outputs an array of tuples, with the ones filled with three uppercase characters representing all sets of 3 consecutive letters that appear in the input. For example, [(), (), (), (), (), (), ('G', 'H', 'I'), ('H', 'I', 'J'), ('I', 'J', 'K'), (), (), (), (), (), (), (), (), (), (), (), (), (), ()]  would be the output for input HIJACKING. This output format has been confirmed to be okay by OP. So has the only uppercase input format. However, if you want to input in only lowercase, simply replace range(65,91) with range(97,123), adding one more byte. Repl.it with all Test Cases! Explanation: Basically what is happening here is: 1. A list, W, is created using W=[*map(chr,range(65,91))], which contains all the uppercase letters in the English alphabet. Because of this, an uppercase input is always required. 2. For each tuple, i, in a list, which we will call U, containing all three consecutive letter tuples, i.e.: U=[('A','B','C'),('B','C','D'),('C','D','E'),...]  created by zip(W,W[1:],W[2:]), each i is fully added to the output list as long as all elements in the set version of i ({*i}) are in the set version of input X ({*X}), i.e. {*X}>={*i}, i.e. X is a superset of i. Otherwise, the empty version of i (()) is added to the list. 3. Once all the tuples have been gone through with the matches fully added, the list is returned as the final output. Scala, 59 bytes (s:Set[Char])=>'a'to'z'sliding 3 filter{_.toSet subsetOf s}  Ungolfed: (s:Set[Char]) => ('a' to 'z').sliding(3).filter{threeChars => threeChars.toSet.subsetOf(s)}  Explanation: (s:Set[Char])=> //define a function with a Set of Chars called s as an argument 'a' to 'z' //create a Range of characters 'a' to 'z' sliding 3 //create an Iterator(Seq(a, b, c), Seq(b, c, d), Seq(c, d, e), ... , Seq(x, y, z)) filter{_.toSet subSetOf s} //keep only the triplets which are a subset of s  Actually, 13 bytes Golfing suggestions welcome. Try it online! S3@╧εjM3úV∩  Ungolfing  Implicit input string s. S sorted(s). 3@╧ Push all length-3 combinations of s. εjM Join all of those combinations into single strings. 3úV Push all slices of the lowercase alphabet of length 1 <= n <= b ∩ Push the intersection of the combinations and slices. Implicit return.  Java 7, 230 bytes String c(char[]q){java.util.Arrays.sort(q);char a[]=new String(q).replaceAll("(.)\\1","$1").toCharArray(),c=97,i=2;String r="",z="",s;for(;c<'z';z+=c++);while(i<a.length)if(z.contains(s=""+a[i-2]+a[i-1]+a[i++]))r+=s+" ";return r;}


This can most likely be golfed, but the challenge was a lot tougher than I originally thought in Java..

Ungolfed & test cases:

Try it here.

class M{
static String c(char[] q){
java.util.Arrays.sort(q);
char a[] = new String(q).replaceAll("(.)\\1", "$1").toCharArray(), c = 97, i = 2; String r = "", z = "", s; for(; c < 'z'; z += c++); while(i < a.length){ if(z.contains(s = "" + a[i-2] + a[i-1] + a[i++])){ r += s+" "; } } return r; } public static void main(String[] a){ System.out.println(c("education".toCharArray())); System.out.println(c("foghorn".toCharArray())); System.out.println(c("cabaret".toCharArray())); System.out.println(c("hijacking".toCharArray())); System.out.println(c("pneumonia".toCharArray())); System.out.println(c("klaxon".toCharArray())); System.out.println(c("perform".toCharArray())); System.out.println(c("learning".toCharArray())); System.out.println(c("dblacghmeifjk".toCharArray())); } }  Output: cde fgh abc ghi hij ijk mno nop abc bcd cde def efg fgh ghi hij ijk jkl klm  • Just have to ask why Java? It isn't the most golfable of languages...? +1 for effort of course... – WallyWest Sep 20 '16 at 9:34 • @WallyWest Well, I'm a Java developer in everyday life. And I know I will never win any challenge with how verbose Java is, but it's still fun to codegolf in Java imho. :) – Kevin Cruijssen Sep 20 '16 at 9:41 • guess ill have to come up with a few creative code challenges in the near future for you to participate :) good work nevertheless! – WallyWest Sep 20 '16 at 9:46 PowerShell v2+, 93 bytes param($n)97..120|%{-join[char[]]($_,++$_,++$_)}|?{(-join([char[]]$n|sort|select -u))-match$_}  Feels much longer than required, but I can't seem to golf it any further. Takes input $n. Loops from 97 to 120, constructing contiguous three-letter strings -- that is, up to the |?, we'll have abc, bcd, cde, etc. on the pipeline. Then that's fed through a Where-Object (the |?) to pull out only those items where the clause is true. Here, the clause is 1) the input string $n, cast as a char-array, sorted and select -unique'd, then -joined back into a string, 2) -matched against the three-letter strings (i.e., regex matching). If it's a match, then the three-letter string is in the word, and so it filters through the |?. The results are left on the pipeline and output is implicit. Examples (Note that here the output is space-separated, since we're stringifying the output by concatenation.) PS C:\Tools\Scripts\golfing> 'education','foghorn','cabaret','hijacking','pneumonia','klaxon','perform','learning'|%{"$_ -> "+(.\close-knit-words.ps1 $_)} education -> cde foghorn -> fgh cabaret -> abc hijacking -> ghi hij ijk pneumonia -> mno nop klaxon -> perform -> learning ->  • Great explanation. I'd give you two up votes if possible. – WallyWest Sep 20 '16 at 13:31 Retina, 106 56 bytes D. O. ^ abc¶ {^(.*)¶.*\1.*$0¶\$1
}T_ll;^.*
2.*¶?



Deduplicate, Sort. Add abc. Find if the substring is found and append if so. Translate to next substring. Repeat. Then remove first two lines.

Try it online

Naive solution:

D.
O.
!&abc|bcd|cde|def|efg|fgh|ghi|hij|ijk|jkl|klm|lmn|mno|nop|opq|pqr|qrs|rst|stu|tuv|uvw|vwx|wxy|xyz


Deduplicate, Sort, then output overlapping matches of 3 sequential letters.

Try it online

• The naïve solution looks pretty straightforward... though I like your golfed solution better... well done! – WallyWest Sep 20 '16 at 23:29

JavaScript (Firefox 48), 93 bytes

x=>[for(c of a=[...new Set(x,i=0)].sort())if(parseInt(d=c+a[++i]+a[i+1],36)%1333==38&!d[3])d]


This lends itself to a 96-byte ES6 version:

x=>[...new Set(x)].sort().map((c,i,a)=>c+a[i+1]+a[i+2]).filter(x=>!x[3]&parseInt(x,36)%1333==38)


How it works

The first major chunk of the function is this:

[...new Set(x)].sort()


new Set(string) creates a Set object that contains one of each unique character in the string. For example, new Set("foghorn") will return Set ["f", "o", "g", "h", "r", "n"]. We can convert this into an array with [... ], then sort it with the built-in .sort(). This turns "foghorn" into ["f", "g", "h", "n", "o", "r"].

The next step is this:

.map((c,i,a)=>c+a[i+1]+a[i+2])


This maps each character in the array to the character concatenated with the two items after it. For example, ["f", "g", "h", "n", "o", "r"] => ["fgh", "ghn", "hno", "nor", "orundefined", "rundefinedundefined"]. (The undefineds pop up when you try to access a non-existent member of the array.

The final step is filtering:

.filter(x=>!c[3]&parseInt(x,36)%1333==38)


First, the !c[3]& is to rule out any strings that contain undefined. This is necessary because a bug causes the following algorithm to count e.g. gmundefined as a consecutive triplet.

All three-consecutive-char strings, when interpreted as base-36 numbers, are 38 modulo 1333. I figured this out by the following calculation:

• 012 (base 36) = 38
• 123 (base 36) = 1371
• 1371 - 38 = 1333
• 1371 mod 1333 ≡ 38 mod 1333 ≡ 38

Therefore, if a three-char string is 38 mod 1333 in base-36, the three characters are consecutive in the alphabet.

Test snippet

function test(x){O.innerHTML=/[^a-z]/.test(x)?"Invalid input":f(x);}
f=x=>[...new Set(x)].sort().map((c,i,a)=>c+a[i+1]+a[i+2]).filter(x=>!x[3]&parseInt(x,36)%1333==38)
<input id=I value="hijacked">
<button onclick="test(I.value)">Run</button>
<pre id=O>cde,hij,ijk</pre>

• This fails for words such as gem and mage. – Neil Sep 20 '16 at 23:55
• So you're saying that all consecutive letter triplets when converted back from hexatrigesimal (base 36) are 38 when mod'd against 1333... that is freaking awesome! – WallyWest Sep 20 '16 at 23:55
• @Neil Fixed at the cost of six bytes. – ETHproductions Sep 21 '16 at 0:06
• I appropriated your !c[3] trick which brought down my ES6 answer to the length of your previous ES6 answer, so now I'm even outgolfing your Firefox 30+ answer. Sorry about that. – Neil Sep 21 '16 at 8:26
• @Neil I don't mind :) – ETHproductions Sep 21 '16 at 13:19

Racket 237 bytes

(define(f s)(let((lr(λ(l i)(list-ref l i)))(l(sort(map char->integer(string->list s))<)))(for((i(-(length l)2)))
(when(=(-(lr l(+ i 2))(lr l(+ i 1)))1(-(lr l(+ i 1))(lr l i)))(for((j 3))(display(string(integer->char(lr l(+ i j))))))))))


Testing:

(f "education")


Output:

cde


Detailed version:

(define(f2 s)
(let ((lr (λ(l i)(list-ref l i)))
(l (sort (map char->integer (string->list s)) <)))
(for ((i (-(length l)2)))
(when (=  (- (lr l (+ i 2)) (lr l (+ i 1)))
1
(- (lr l (+ i 1)) (lr l i)))
(for((j 3))
(display (string(integer->char (lr l (+ i j))))))))))


Ruby, 50 bytes

each_cons(3) gets all consecutive sublists of length 3 from the alphabet ?a..?z, then use e&s.chars==e to select only the ones that have all characters in the target string by using setwise intersection. Returns a list of lists.

->s{(?a..?z).each_cons(3).select{|e|e&s.chars==e}}


Try it online!

[R], 110 bytes

 f=function(b){a=combn(sort(utf8ToInt(b)),3);apply(unique(t(a[,which(apply(diff(a),2,prod)==1)])),1,intToUtf8)}


I'm sure its still golfable

Japt, 10 9 bytes

;Cã3 fUá3

;Cã3 fUá3     :Implicit input of string U
;C            :Lowercase alphabet
ã3          :Substrings of length 3
f        :Filter elements contained in
Uá3     :Permutations of U of length 3
`