Is it a wordinian?

20

3

What is the shortest way to see if an input is a wordinian using any programming language?

A wordinian is a word that contains words of length 1 to the original word's length. For example,

bin

'I' is a word
'in' is a word
'bin' is a word

Or,

stage

'a' is a word
'ta' is a word (yes it is)
'age' is a word
'stag' is a word
'stage' is a word

Input

Your code should take a word and a dictionary as inputs, in any reasonable format.

Output

The output should be a value to indicate true or false, to tell us if the word is a wordinian.

For more info on wordinians click here.

Here is a list of words that I will be using as inputs and subwords. Also, in response to @xnor, it has to contain subwords of each length, not a chain of subwords. Note that only one word will be used as an input.

Jacques Marais

Posted 2016-08-15T17:56:28.770

Reputation: 303

@FryAmTheEggman I can't put a whole dictionary on here. What if it's any word that exists? – Jacques Marais – 2016-08-15T18:00:13.097

6I'd recommend passing in a dictionary as input. That way, its easy to come up with test cases (as you can make your own small dictionary) – Nathan Merrill – 2016-08-15T18:04:37.467

2Does it just have to contain subwords of each length, or do they have to be a chain where each subword adds one letter to the previous? – xnor – 2016-08-15T18:07:51.250

@FryAmTheEggman I edited my question to supply a list of all words. – Jacques Marais – 2016-08-15T18:25:08.370

@xnor I updated the question to answer your question. – Jacques Marais – 2016-08-15T18:25:24.713

@trichoplax Ok thanks. Also, will something like this list work?

– Jacques Marais – 2016-08-15T18:31:40.103

@trichoplax Actually, I think this list should work: https://raw.githubusercontent.com/dwyl/english-words/master/words.txt
I updated my question to include the new list.

– Jacques Marais – 2016-08-15T18:35:54.310

@trichoplax The new list contains about 479k words. I think that should be enough. How can this question get reopened though? – Jacques Marais – 2016-08-15T18:38:34.733

@trichoplax Ok thanks for all your help! – Jacques Marais – 2016-08-15T18:39:48.310

@JacquesMarais This challenge is clear to me, but I'd definitely include some test cases! – Nathan Merrill – 2016-08-15T18:40:52.470

@trichoplax Ok I use the ENABLE2K list now, thanks. So you mean a to take the whole list as an input and find the wordinians, instead of taking one word as an input? – Jacques Marais – 2016-08-15T18:50:22.047

1@JacquesMarais the concept is to take a word and a dictionary, and return true if the word is a wordinian (according to the dictionary) – Nathan Merrill – 2016-08-15T18:52:08.357

@NathanMerrill I don't think I understand you correctly now. So you use one word and check through the dictionary to see if the word is a wordinian? – Jacques Marais – 2016-08-15T18:53:20.390

Yep. Aka, if your dictionary was ["bin","i"], then "bin" wouldn't be a wordinian because "in" isn't in the dictionary. The function call would be func("bin",["bin","i"]) – Nathan Merrill – 2016-08-15T18:55:37.060

Oh, ok, I see @NathanMerrill. Thanks, I'll use that instead. But how can implement that into the question? – Jacques Marais – 2016-08-15T18:56:51.580

I've edited to suggest how you might word it, rather than trying to discuss it in comments too much. This is just a suggestion so feel free to reverse the edit or amend it to suit your intention. – trichoplax – 2016-08-15T19:08:55.547

What do you mean by "contains"? From the examples it seems to be "contains a substring" rather than "contains a subsequence", but it would be good to make that explicit. – Peter Taylor – 2016-08-15T20:06:09.997

Can I assume the length of the word and the dictionary (and all entries in the dictionary) have length of at least 1? – Οurous – 2016-08-15T22:50:15.090

Does the output have to be a value? Or can it be something else (such as an array) according to our definition of truthy/falsy?

– Luis Mendo – 2016-08-15T22:56:41.060

@JacquesMarais UrbanDictionary hardly counts as a valid source for the English language. – mbomb007 – 2016-08-16T21:50:08.403

Three of the ten answers beat the accepted answer in bytes. This is a code-golf, and as such the shortest answer should be the accepted answer. Was there a different winning criterion? – Steven H. – 2016-08-18T19:18:20.417

@JacquesMarais No problem! For the future, if you want to select the answer with the highest votes us here at PPCG call that a Popularity-Contest.

– Steven H. – 2016-08-20T16:46:08.877

Answers

4

Pyth, 20 16 15 13 11 bytes

Thanks to Leaky Nun for saving 4 bytes! Unfortunately, I changed the entire method afterwards, but it still helped.

gl{lM}#zQlz

Expects input as dictionary followed by word. Outputs True or False.

Try it here!

Explanation:

        lz   Collects the length of the word  input
g             and compares it to:
 l             The length of the following:
     # Q        Select all words from the dictionary that
    } z         are contained within the input word.
  lM            Map them to their respective lengths, and
 {              then remove any duplicates.

This does not function if the empty string "" is a valid word.

Steven H.

Posted 2016-08-15T17:56:28.770

Reputation: 2 841

1.E can be replaced with s – Leaky Nun – 2016-08-15T22:40:04.583

1m}kH can be replaced with }RH – Leaky Nun – 2016-08-15T22:40:51.163

1

I would just give you the golfed code

– Leaky Nun – 2016-08-15T22:42:10.943

11

Python, 52 bytes

lambda w,d:len({len(x)for x in d if x in w})==len(w)

An anonymous function that takes a word w and dictionary d. It takes the words in d that are substrings of w, makes a set of their lengths, and then checks that there as many distinct lengths as there are letters in w.

xnor

Posted 2016-08-15T17:56:28.770

Reputation: 115 687

Ugh I just wrote the exact same thing except I had W instead of x and [ instead of {. +1 – Daniel – 2016-08-16T16:31:02.493

@Dopapp It wouldn't work if you used [ instead of {. {...} is a set comprehension (The same as set([...])). – mbomb007 – 2016-08-16T21:51:29.873

@mbomb007, oh right, a set would be needed – Daniel – 2016-08-16T22:03:58.560

@xnor Sorry for not selecting this answer, but as it's a code golf, I have to select the shortest... – Jacques Marais – 2016-08-20T13:22:21.147

4

Python 3, 108 bytes

lambda w,d,r=range:all(any(i in d for i in j)for j in[[w[i:i+s]for i in r(len(w)+1-s)]for s in r(1,len(w))])

An anonymous function that takes input, via argument, of a word w as a string and a dictionary d as a list of strings and returns True or False.

How it works

The first step is a list comprehension that generates a list of lists of all substrings of w excluding w, grouped by length. For example, for 'stage', the list [['s', 't', 'a', 'g', 'e'], ['st', 'ta', 'ag', 'ge'], ['sta', 'tag', 'age'], ['stag', 'tage']] is generated. This is achieved by looping over all valid start indices i for each substring length s, and slicing every s-length substring using w[i:i+s]. For each list in this list, the presence of each substring in the dictionary is checked; calling any returns a hit if at least one match for a given length is found. Finally, calling all checks if a match has been found for all substring lengths, and the result of this is returned.

Try it on Ideone

TheBikingViking

Posted 2016-08-15T17:56:28.770

Reputation: 3 674

4

Ruby, 44 bytes

  • 7 Bytes off thanks to @NotThatCharles and his set operator tricks!
  • 2 bytes off thanks to @Jordan with the Ruby 2.3 safe navigation operator trick w[x]&.size :)
->w,d{[*1..w.size]-d.map{|x|w[x]&.size}==[]}

It's an anonymous functions which takes a word w and a dictionary (array of words) d. Creates two arrays: The first containing the numbers 1 up to and including the length of w; The second array is d with each word mapped to their size if they are a substring of w, otherwise nil. Then it does set substraction to check whether the second array contains all element of the first array.

daniero

Posted 2016-08-15T17:56:28.770

Reputation: 17 193

1With the "safe navigation operator" in Ruby 2.3 you can save a couple bytes: w[x]&.size==i instead of x.size==i&&w[x]. – Jordan – 2016-08-15T20:55:54.850

Oh wow, thanks @Jordan. Didn't know that one, awesome :) – daniero – 2016-08-15T20:59:03.120

1you can save another few bytes in your anonymous function (and maybe the full program) by dropping the uniq and -[p] and using set subtraction instead: [*1..w.size]-d.map{...}==[] – Not that Charles – 2016-08-16T16:37:13.620

@NotthatCharles That's brilliant! Thanks :) – daniero – 2016-08-16T20:51:28.790

3

PowerShell v3+ v2+, 127 110 70 65 bytes

param($a,$d)($d|?{$a-match$_}|select length -U).count-eq$a.length

(I see now that my approach is similar to @xnor's, though I developed it independently)

Takes input word $a and dictionary $d, expecting $d as an array (see examples below). Loops through the entirety of $d and performs a Where-Object to pull out the entries where the current word $_ is a regex -match against the input word $a (i.e., is the current word a substring of the input word).

We collect all of those substring words and pipe them to Select-Object on the length parameter and the -Unique constraint. That will pull out the unique lengths of each substring. For example, for input word comb, this will be an array of (4,2) for ('comb','om').

We take the .count of that resultant array and compare it against the input word's .length. If it's equal to, that means that every substring length is in the dictionary, so $TRUE, otherwise we're missing at least one, so $FALSE. That Boolean value is left on the pipeline and output is implicit.

NB - This should work in v2+, since the -in operator is no longer present, but I've not tested that version.

Examples

PS C:\Tools\Scripts\golfing> .\is-it-a-wordinian.ps1 'stage' (gc .\words.txt)
True

PS C:\Tools\Scripts\golfing> .\is-it-a-wordinian.ps1 'metal' (gc .\words.txt)
True

PS C:\Tools\Scripts\golfing> .\is-it-a-wordinian.ps1 'comb' (gc .\words.txt)
False

AdmBorkBork

Posted 2016-08-15T17:56:28.770

Reputation: 41 581

2

Perl, 86 bytes

Requires -E at no extra cost.

chop(($s,@d)=<>);for$=(1..($x=length$s)){$-+=!!grep$s=~/$_/,grep$===y///c,@d}say$-==$x

Accepts all input via STDIN. First input is target word, rest of input is dictionary. Prints 1 on success, empty string on failure.

Usage

perl -E 'chomp(($s,@d)=<>);for$=(1..($x=length$s)){$-+=!!grep$s=~/$_/,grep$===y///c,@d}say$-==$x' <<< 'stage
a
ta
age
stag
stage'
1
perl -E 'chomp(($s,@d)=<>);for$=(1..($x=length$s)){$-+=!!grep$s=~/$_/,grep$===y///c,@d}say$-==$x' <<< 'stage
a
at
age
stag
stage'

perl -E 'chomp(($s,@d)=<>);for$=(1..($x=length$s)){$-+=!!grep$s=~/$_/,grep$===y///c,@d}say$-==$x' <<< 'bin
i
in
bin'
1

Dom Hastings

Posted 2016-08-15T17:56:28.770

Reputation: 16 415

2

Mathematica, 90 bytes

Sort[MemberQ[DictionaryWordQ/@StringPartition[#,t,1],True]~Table~{t,StringLength@#}][[1]]&

Uses Mathematica's builtin DictionaryWordQ.

Taking input d as dictionary is 5 bytes shorter, but much slower for long lists:

m=MemberQ;Sort[m[d~m~#&/@StringPartition[#,t,1],True]~Table~{t,StringLength@#}][[1]]&

martin

Posted 2016-08-15T17:56:28.770

Reputation: 1 335

2

MATL, 15 bytes

1 byte saved using an idea from @xnor's answer.

XXgc32>!suz1Gn=

Outputs 1 or 0 for truthy or falsy.

Try it online!

XX      % Take the two inputs implicitly. Apply the second as a regex into the
        % first. Since the second input is a cell array, each of its contents is
        % applied separately as a regex. So for each dictionary word ("sub-word") 
        % this outputs the sub-word if found in the original word, or else an 
        % empty array. Gives a cell array of cells of strings
g       % Remove one level of nestedness
c       % Convert to char. This concatenates all found sub-words as rows of a 2D 
        % char array, padding with spaces as needed
32>!s   % For each row, count how many non-space characters there are. This is 
        % the length of each sub-word
uz      % Number of distinct non-zero elements
1Gn     % Push length of the original word
=       % Are they equal? Implicitly display

Luis Mendo

Posted 2016-08-15T17:56:28.770

Reputation: 87 464

1

05AB1E, 8 bytes

ŒÃ€gZLåP

Word as first input, dictionary-list as second input.

Try it online or verify a few more test cases.

Explanation:

Œ         # Get all possible substrings of the (implicit) input-string
 Ã        # Only keep the ones which are also in the (implicit) dictionary-list
  €g      # Get the length of each remaining string
    Z     # Push the maximum length (without popping the list)
     L    # Pop and push a list in the range [1, maximum-length]
      å   # Check for each value if it's in the list of lengths
       P  # And check if this is truthy for all
          # (then output the result implicitly as result)

Kevin Cruijssen

Posted 2016-08-15T17:56:28.770

Reputation: 67 575

1

Kotlin, 51 bytes

{w,d->w.indices.all{w.windowed(it+1).any{it in d}}}

Try it online!

snail_

Posted 2016-08-15T17:56:28.770

Reputation: 1 982

1

JavaScript (Firefox 30-57), 68 bytes

(w,a)=>new Set((for(x of a)if(~w.search(x))x.length)).size==w.length

Using a generator comprehension avoids creating an intermediate array. 73 byte ES6 version:

(w,a)=>new Set(a.filter(x=>~w.search(x)).map(x=>x.length)).size==w.length

Neil

Posted 2016-08-15T17:56:28.770

Reputation: 95 035

1

Perl, 42 41 bytes

Includes +2 for -p0

Give word followed by the dictionary on STDIN:

(echo stage; cat dictionary.txt) | ./wordinian.pl

(When testing on unix make sure dictionary.txt uses \n as line terminator, not \r\n)

wordinian.pl:

#!/usr/bin/perl -p0
s%\G.%!/^.*(.{$+[0]})\H*
\1
/%eg;$_=!//

Ton Hospel

Posted 2016-08-15T17:56:28.770

Reputation: 14 114

0

SQF, 147 bytes

Using the function-as-a-file format:

params["w","d"];a=[];c=count w;for"i"from 1 to c do{a=a+[""];for"j"from 0 to c-i do{q=w select[j,i];if(q in d)then{a set[i-1,q]}}};c==count(a-[""])

Call as: ["WORD", DICTIONARY] call NAME_OF_COMPILED_FUNCTION

Ungolfed:

//name parameters
params["w", "d"];
a = []; c = count w;
//for each length of subword
for "i" from 1 to c do {
    //add entry to the `a`
    a = a + [""];
    //for each starting position for that length
    for "j" from 0 to c - i do {
        //get subword
        q = w select [j, i];
        //check if in dictionary
        if(q in d) then {
            //set the entry to the wubword
            a set [i - 1, q]
        }
    }
};
//check if length of word is equal to number of subwords
c == count (a - [""])

Οurous

Posted 2016-08-15T17:56:28.770

Reputation: 7 916