25
\$\begingroup\$

In this code golf challenge, you will verify hashtags!

#What_your_code_should_do

Input is a string. Output a truthy value if it is a valid hashtag, and a falsy value otherwise.

We define a string as a valid Hashtag if ...

  • It starts with a hash (#).
  • It doesn't have a number right after the hashtag (e.g. #2016USElection isn't a valid hashtag).
  • It doesn't have any "special characters" (i.e. any character that isn't an alphabet, underscore (_) or a number).

You can assume that the input only contains ASCII characters. (It would be unfair if we did Unicode too.)

#Rules

Basic rules apply.

#Examples

Truthy:

#
#e
#_ABC 
#thisisanunexpectedlylongstringxoxoxoxo
#USElection2016

Falsy:

Hello, World!
#12thBday
#not-valid
#alsoNotValid!
#!not_a_hash
\$\endgroup\$
  • 10
    \$\begingroup\$ Is # really a valid hashtag? \$\endgroup\$ – Adám Jul 18 '16 at 11:35
  • 4
    \$\begingroup\$ Is #öäü valid? \$\endgroup\$ – chrki Jul 18 '16 at 12:15
  • 7
    \$\begingroup\$ # is not a valid hashtag by any system, Facebook or Twitter it also breaks the rules set also im not sure #_ABC is valid again on them but im not certain of that. \$\endgroup\$ – Martin Barker Jul 18 '16 at 13:23
  • 3
    \$\begingroup\$ I assume an alphabet means ascii uppercase or lowercase letter? i.e. abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ? \$\endgroup\$ – Rɪᴋᴇʀ Jul 18 '16 at 14:14
  • 7
    \$\begingroup\$ A # is not a hashtag. It's a hash. It, followed by a string is what social media networks refer to as a hashtag. It's a tag, which starts with a hash. \$\endgroup\$ – i-CONICA Jul 18 '16 at 15:43

28 Answers 28

19
\$\begingroup\$

Retina, 12 bytes

^#(?!\d)\w*$

Prints 1 for hashtags and 0 otherwise.

Try it online! (The first line enables a linefeed-separated test suite.)

Not much to explain here, this is quite a literal implementation of the definition: ^ and $ are just anchors ensuring that the match covers the entire string, # checks that the string starts with a #, (?!\d) ensures that the next character isn't a digit (without advancing the regex engine's position), \w* checks that we can reach the end of the string with zero or more letters, digits or underscores.

By default, Retina counts the number of matches of the given regex, which is why this gives 1 for valid hash tags and 0 otherwise.

\$\endgroup\$
  • \$\begingroup\$ In Perl, (?!\d) is (?=\D)... but I don't know how you've written Retina. Is it possible you could use (?\D) without the = and save a byte? (If not, is it worth editing the language so that's doable?) \$\endgroup\$ – msh210 Jul 19 '16 at 15:08
  • 2
    \$\begingroup\$ @msh210 (?!\d) is different from (?=\D) in that the latter requires some character after the current position while the former is satisfied with the end of the string. Regardless of that, adjusting the regex flavour is currently not possible (since I'm just handing the regex to .NET's regex engine), but making such changes is on the roadmap somewhere (very far) down the line. \$\endgroup\$ – Martin Ender Jul 19 '16 at 15:13
  • 1
    \$\begingroup\$ That said, I don't think I'll make the = optional. The entire (?...) syntax was chosen for extensibility, in that the character after the ? is never optional and determines which kind of group this is, and I don't think I want to give up that extensibility. \$\endgroup\$ – Martin Ender Jul 19 '16 at 15:13
  • \$\begingroup\$ (re your first comment) Duh, of course, I shoulda noted that. But it's irrelevant to this answer. (re your second) Yeah, makes sense. There is after all also (?{ and (?? and (?< (both for capturing groups and for lookbehind) and (?- and (?1 and of course the basic (?:. And maybe some I;ve missed. \$\endgroup\$ – msh210 Jul 19 '16 at 15:19
6
\$\begingroup\$

Perl, 22 bytes

21 bytes code +1 for -p

$_=/^#([a-z_]\w*)?$/i

Prints 1 if it's a valid hashtag, empty string otherwise.

Usage

perl -pe '$_=/^#([a-z_]\w*)?$/i' <<< '#'
1
perl -pe '$_=/^#([a-z_]\w*)?$/i' <<< '#_test'
1
perl -pe '$_=/^#([a-z_]\w*)?$/i' <<< '#1test'

Saved 2 bytes thanks for Martin Ender (and another 4 using his lookaround method)


Perl, 18 bytes

17 bytes code +1 for -p

Using Martin's lookaround this can be much shorter!

$_=/^#(?!\d)\w*$/
\$\endgroup\$
  • \$\begingroup\$ You copied Martin's one and edited it, right? \$\endgroup\$ – user54200 Jul 19 '16 at 6:54
  • \$\begingroup\$ @MatthewRoh The second answer uses Martin's mechanism yes. He did say I could use it, but I didn't want it to be my main answer as I didn't come up with it myself! I've added it for comparison. Retina still beats Perl easily in this type of challenge! \$\endgroup\$ – Dom Hastings Jul 19 '16 at 6:56
6
\$\begingroup\$

JavaScript (ES6), 25 bytes

s=>/^#(?!\d)\w*$/.test(s)

F = s => /^#(?!\d)\w*$/.test(s)
input.oninput = () => result.innerHTML = input.value ? F(input.value) ? '\ud83d\udc8e' : '\ud83d\udca9' : '\ud83d\udcad';
#input, #result {
  vertical-align: middle;
  display: inline-block;
}
#input {
  line-height: 2em;
}
#result {
    font-size: 2em;
}
<input id="input" type="text"/> <span id="result">&#x1f4ad</span>

\$\endgroup\$
5
\$\begingroup\$

C, 80 bytes

Function f() takes the string as an argument and modifies int *b to either 1 or 0 to indicate truthy/falsy.

f(char*p,int*b){for(*b=(*p==35)&&!isdigit(p[1]);*p;++p)*b&=isalnum(*p)||*p==95;}

If the string always has at least one character (i.e. never an empty string), a byte can be shaved off for 79 bytes:

f(char*p,int*b){for(*b=(*p==35)&&!isdigit(p[1]);*++p;)*b&=isalnum(*p)||*p==95;}
\$\endgroup\$
5
\$\begingroup\$

Python 3, 41 bytes

import re
re.compile('#(?!\d)\w*$').match
\$\endgroup\$
  • \$\begingroup\$ This should be absolutely fine. Since match objects are truthy and None is falsey, I think dropping the bool() is okay. \$\endgroup\$ – Lynn Jul 19 '16 at 14:42
  • \$\begingroup\$ Yeah I thought about that, thanks for clarifying it! \$\endgroup\$ – Gábor Fekete Jul 19 '16 at 14:44
  • \$\begingroup\$ This generates truthy value for “#fix me Gábor” too. BTW, I see the rules are getting ignored by others too, but this we used to consider a snippet, which usually is not accepted as answer unless the question explicitly allows them. \$\endgroup\$ – manatwork Jul 19 '16 at 14:57
  • \$\begingroup\$ Thanks, I rewrote it to handle the case you wrote and made it a lambda function. \$\endgroup\$ – Gábor Fekete Jul 19 '16 at 15:00
  • 2
    \$\begingroup\$ How about re.compile('#(?!\d)\w*$').match? It’s acceptable to drop the f=, BTW. \$\endgroup\$ – Lynn Jul 19 '16 at 16:19
4
\$\begingroup\$

Brachylog, 55 bytes

"#"|h"#",?b@lL'(eE,@A:"1234567890":"_"c'eE),@A:"_"ce~hL

This uses no regex.

Explanation

Main predicate, Input (?) is a string

  "#"                           ? = "#"
|                             Or
  h"#",                         First character of ? is "#"
  ?b@lL                         L is the rest of the chars of ? lowercased
  '(                            It is not possible for any char of L that...
    eE,                           Call this char E
    @A:"1234567890":"_"c          Concatenate the lowercase alphabet with the digits and "_"
    'eE                           E is not a member of that concatenated string
   ),                           
   @A:"_"c                      Concatenate the lowercase alphabet with "_"
   e~hL                         One char of that concatenated string is the first char of L
\$\endgroup\$
4
\$\begingroup\$

Python 3, 103 93 bytes

all((c=='_'or c.isalpha()*i>0)^(i<1and'#'==c)^(c.isdigit()*i>1)for i,c in enumerate(input()))

The # being True killed me here, I had to enumerate the string to avoid an index error on the single character input.

\$\endgroup\$
  • 1
    \$\begingroup\$ +1. Nice! I completely forgot the isalpha() method on my py3 answer :D "#" being true, also destroyed me. \$\endgroup\$ – Yytsi Jul 18 '16 at 13:59
4
\$\begingroup\$

PowerShell v2+, 25 bytes

$args-match'^#(?!\d)\w*$'

Using Martin's regex, just wrapped up in PowerShell's -match operator coupled with the input $args. For truthy/falsey values, this will return the string itself on a match (a truthy value) or nothing on a non-match (a falsey value). This is because when a comparison operator is applied against an array, it returns anything that satisfies that operator.

A couple examples (wrapped in a [bool] cast to make the output more clear):

PS C:\Tools\Scripts\golfing> [bool](.\hashtag-or-not.ps1 '#2016Election')
False

PS C:\Tools\Scripts\golfing> [bool](.\hashtag-or-not.ps1 'Hello, World!')
False

PS C:\Tools\Scripts\golfing> [bool](.\hashtag-or-not.ps1 '#')
True

PS C:\Tools\Scripts\golfing> [bool](.\hashtag-or-not.ps1 '')
False

PS C:\Tools\Scripts\golfing> [bool](.\hashtag-or-not.ps1 '#USElection2016')
True
\$\endgroup\$
3
\$\begingroup\$

Mathematica, 52 46 43 bytes

Saved 6 9 bytes due to @MartinEnder.

StringMatchQ@RegularExpression@"#(?!\d)\w*"

Function. Takes a string as input, and returns True or False as output. Pretty simple, just matches against the regex /#(?!\d)\w*/.

\$\endgroup\$
  • \$\begingroup\$ I have reason to believe that this won't work for inputs like hello#world since you don't have the beginning and end string anchors. I don't know Mathematica though so I'm not sure. \$\endgroup\$ – Value Ink Jul 18 '16 at 17:21
  • \$\begingroup\$ All righty, I can live with that. Have your +1 \$\endgroup\$ – Value Ink Jul 18 '16 at 20:39
3
\$\begingroup\$

Dyalog APL, 22 20 bytes

Without RegEx:

{0≤⎕NC 1↓⍵,⎕A}∧'#'=⊃

-2 thanks to ngn

\$\endgroup\$
  • 1
    \$\begingroup\$ Oh, wow. There are still people who know APL. It's 37 years since I used it! \$\endgroup\$ – Auspex Jul 19 '16 at 10:39
  • \$\begingroup\$ @Auspex APL is well and alive, but quite few features have been added in those years. \$\endgroup\$ – Adám Jul 19 '16 at 11:17
3
\$\begingroup\$

Python 2, 79 bytes

lambda x:x=='#'or(1>x[1].isdigit())&x[1:].replace('_','').isalnum()&('#'==x[0])

First golfing attempt. Ungolfed version:

def f(x):
    if x == '#':
        return True
    else:
        return x[0]=='#' and x[1:].replace('_','').isalnum() and not x[1].isdigit()
\$\endgroup\$
  • \$\begingroup\$ Nice answer, and welcome to the site! \$\endgroup\$ – DJMcMayhem Jul 18 '16 at 17:24
3
\$\begingroup\$

Octave, 37 56 54 43 bytes

Thanks to @LuisMendo for removing 8 bytes!

@(s)s(1)==35&(isvarname(s(2:end))|nnz(s)<2)

Not very golfy, but very built-inny.
Edit: The original code accepted strings with no leading '#'. I guess I should have stuck with regex.

Test suite on ideone.

\$\endgroup\$
3
\$\begingroup\$

Python3 - 156 128 bytes

lambda n:n=="#"or(n[0]=="#")*all(any([47<ord(c)<58,64<ord(c)<91,ord(c)==95,96<ord(c)<123])for c in n[1:]+"0")*~(47<ord(n[1])<58)

A solution that doesn't use regex. 0 is falsey and every other value is truthy.

Thanks to @LeakyNun for saving bytes!

\$\endgroup\$
  • \$\begingroup\$ @LeakyNun I had to remove the +0 after n[1:], but sadly, still didn't work :/ Gave false to "#d". \$\endgroup\$ – Yytsi Jul 18 '16 at 15:32
  • \$\begingroup\$ @LeakyNun still doesn't work :( Again, had to remove +0 but fails on "#d". I tested it on Python3 though. Not sure if it will work on Python2 \$\endgroup\$ – Yytsi Jul 18 '16 at 15:37
  • \$\begingroup\$ @LeakyNun Just plain false. \$\endgroup\$ – Yytsi Jul 18 '16 at 15:47
  • \$\begingroup\$ @LeakyNun Throws IndexOutOfRange for "#" and False for "#d". \$\endgroup\$ – Yytsi Jul 18 '16 at 16:10
  • \$\begingroup\$ lambda n:n=="#"or(n[0]=="#")*all(any([47<ord(c)<58,64<ord(c)<91,ord(c)==95,96<ord(c)<123])for c in n[1:]+"0")*~(47<ord(n[1])<58) for 128 bytes. Proof that it works \$\endgroup\$ – Leaky Nun Jul 18 '16 at 16:44
2
\$\begingroup\$

Lua, 59 55 54 bytes

Code

s=arg[1]print(load(s:sub(2).."=0")and s:sub(1,1)=="#")

How it works:

  1. Check if the rest of the characters can be a valud Lua identifier (identifiers in Lua follow the same rules as hashtags.)
  2. Check if the first character is a #.

Takes input from the command line. Prints true if the string is a valid hashtag, otherwise, it prints nil.

\$\endgroup\$
2
\$\begingroup\$

Google Sheets, 30 bytes

An anonymous worksheet function that takes input from the cell A1 checks it against the RE2 expression and outputs the result to the calling cell.

=RegexMatch(A1,"^#([a-z_]\w*)?
\$\endgroup\$
1
\$\begingroup\$

05AB1E, 18 bytes

Code:

¬'#Qs¦A«¬d_sDžjKQP

Uses the CP-1252 encoding. Try it online!.

\$\endgroup\$
1
\$\begingroup\$

Sed 19 + 2 = 21 bytes

/^#([a-z_]\w*)?$/Ip

This filters out all non-hashtags and outputs valid hashtags.

Run as sed -rn "/^#$|^#[a-z]\w*$/Ip". Quit with Ctrl + D (send EOF).

\$\endgroup\$
1
\$\begingroup\$

GNU grep, 15 + 2 = 17 bytes

grep -Ei '^#([a-z_]\w*)?$'

Test:

$ echo '#
#e
#_ABC
#thisisanunexpectedlylongstringxoxoxoxo
#USElection2016
Hello, World!
#12thBday
#not-valid
#alsoNotValid!' | grep -Ei '^#([a-z_][a-z0-9_]*)?$'

Output:

#
#e
#_ABC
#thisisanunexpectedlylongstringxoxoxoxo
#USElection2016
\$\endgroup\$
1
\$\begingroup\$

Python 3, 97 Bytes 70 Bytes 56 Bytes

lambda x:s=x[2:];b=x[1];all(x!="#",[x[0]=="#",any[b.isalpha(),"_"in b],any[s.isalnum(),"_"in s]])

(Code changed) Human readable

x=input()
if x[0]=="#" and x[1].isalpha() and str(x)[2:].isalnum():
    print(True)
else:
    print(False)

\$\endgroup\$
  • \$\begingroup\$ Nice answer, and welcome to the site! Functions are also allowed, so you could shorten this quite a bit with lambda x:all(True==[x[0]=="#",x[1].isalpha(),x[2:].isalpha()]) \$\endgroup\$ – DJMcMayhem Jul 18 '16 at 20:46
  • \$\begingroup\$ No problem, glad I could help! \$\endgroup\$ – DJMcMayhem Jul 18 '16 at 21:01
  • 1
    \$\begingroup\$ I hate to be the bringer of bad news, but doesn't this fail for '#', which the OP says is truthy? Won't it also fail if the hashtag contains any underscores, which are false under isalpha? \$\endgroup\$ – TheBikingViking Jul 18 '16 at 21:23
  • \$\begingroup\$ @TheBikingViking sorry, I will try to fix this now \$\endgroup\$ – Dignissimus - Spammy Jul 18 '16 at 21:26
  • 2
    \$\begingroup\$ @TheBikingViking That's not what non-competing means. Non-competing is not an excuse for an invalid submission. The correct procedure is to delete the answer, fix it, then undelete it. \$\endgroup\$ – Mego Jul 18 '16 at 22:44
1
\$\begingroup\$

Pyke, 19 bytes

\#.^It,!It\_D-PRkq|

Try it here!

Quick fix for tonight

\$\endgroup\$
  • 1
    \$\begingroup\$ @kenorb rebooted it, ping me if any more issues \$\endgroup\$ – Blue Jul 18 '16 at 22:15
  • \$\begingroup\$ #123 returns still nothing, shouldn't return 0? \$\endgroup\$ – kenorb Jul 18 '16 at 22:25
  • 1
    \$\begingroup\$ Nothing is a boolean false \$\endgroup\$ – Blue Jul 18 '16 at 22:25
1
\$\begingroup\$

Ruby, 16 + 3 1 (n flag) = 19 17 bytes

Uses 0 as truthy and nil as falsy.

p~/^#(?!\d)\w*$/

Run it as ruby -ne 'p~/^#(?!\d)\w*$/'. Thanks to @manatwork for fixing the bash error when running the program.

\$\endgroup\$
  • 1
    \$\begingroup\$ Do yourself a favor and always enclose code in single quotes. Otherwise the shell will attempt (or even worse, successfully perform) all kind of expansions. (Regarding the current issue with !, see Event Designators in man bash.) \$\endgroup\$ – manatwork Jul 19 '16 at 7:55
1
\$\begingroup\$

Standard ML, 121 118 107 bytes

(fn#"#"::a=>(fn x::r=>x> #"@"andalso List.all(fn#"_"=>1=1|c=>Char.isAlphaNum c)a|e=>1=1)a|e=>1=0)o explode;

Try it online! Functional solution without using regex. Declares an anonymous function which is bond to the implicit result identifier it.

> val it = fn : string -> bool    
- it "#valid_hash";
> val it = true : bool
\$\endgroup\$
  • 4
    \$\begingroup\$ isAlphaNum$orelse that's rather threatening... \$\endgroup\$ – cat Jul 18 '16 at 15:57
  • \$\begingroup\$ @cat this might be the sole positive thing one can say about such verbose boolean operators as orelse and andalso. \$\endgroup\$ – Laikoni Jul 18 '16 at 17:38
  • 2
    \$\begingroup\$ It's like, AlphaNum, orelse!! (orelse what?) \$\endgroup\$ – cat Jul 18 '16 at 17:43
  • \$\begingroup\$ One might consider the o explode at the end to be quite threatening too ... \$\endgroup\$ – Laikoni Jul 18 '16 at 17:48
  • 1
    \$\begingroup\$ SML seems quite scary, I don't think I could handle that all day :c \$\endgroup\$ – cat Jul 18 '16 at 18:10
1
\$\begingroup\$

Excel VBA, 54 bytes

Anonymous VBE immediate window function that takes input from cell [A1], checks if the value of the cell matches the Like pattern, and outputs as Boolean to the VBE immediate window

?Not[Left(A1,2)]Like"[#]#"And[A1]Like"[#][_a-zA-z0-9]*
\$\endgroup\$
1
\$\begingroup\$

Haskell, 79 bytes

import Data.Char
f(a:r)=a=='#'&&r==""||r!!0>'@'&&all(\c->c=='_'||isAlphaNum c)r

Try it online!

\$\endgroup\$
0
\$\begingroup\$

C#, 92 bytes

s=>s[0]=='#'&s.Length>1&&(s[1]<48|s[1]>57)&s.Skip(1).All(x=>char.IsLetterOrDigit(x)|x=='_');

C# lambda (Predicate) where input is a string and output is a bool.

Try it online!

\$\endgroup\$
0
\$\begingroup\$

Lua, 39 bytes

print(arg[1]:match("^#[%a_][%a_%d]*$"))

Straightforward copypasta of match description. Outputs falsy nil if not hashtag, outputs truthly hashtag back otherwise.

Can be shortened for one more byte by using find if outputing list of two values (which is truthly) doesn't break rules.

\$\endgroup\$
  • \$\begingroup\$ I think this won't match a # on its own. \$\endgroup\$ – Martin Ender Jul 19 '16 at 14:44
  • \$\begingroup\$ @MartinEnder, of course. It shouldn't. None of the top answers do that either. Also codegolf.stackexchange.com/questions/85619/hashtag-or-not/… \$\endgroup\$ – Oleg V. Volkov Jul 20 '16 at 0:09
  • \$\begingroup\$ Whether # is a hashtag on Twitter or Facebook is irrelevant to this challenge. The specification is very clear on the fact that # should be considered a hashtag for the purposes of this challenge. And while I haven't checked all of the answers, all I did check do accept # as a hashtag, so I'm not sure which top answers you're referring to. \$\endgroup\$ – Martin Ender Jul 20 '16 at 7:12
0
\$\begingroup\$

Clojure, 130 135 132 bytes

  • +5 bytes to deal with an NPE that happened when the string consisted of only a hashtag.

  • -2 bytes by using Character/isLetterOrDigit.

(fn[s](let[[h & r]s n(map int r)](and(= h\#)(not(<= 48(or(first n)0)57))(every? #(or(Character/isLetterOrDigit^long %)(= 95 %))n))))

Ungolfed:

(defn hashtag? [s]
  (let [[h & r] s
        codes (map int r)]
    (and (= h \#)
         (not (<= 48 (or (first codes) 0) 57))
         (every?
           #(or (Character/isLetterOrDigit ^long %)
                (= 95 %))
           codes))))
\$\endgroup\$
  • \$\begingroup\$ Whoops, this actually gives a NPE for "#". Give me a sec. \$\endgroup\$ – Carcigenicate Nov 29 '16 at 20:08
0
\$\begingroup\$

Java 8, 57 54 28 bytes

s->s.matches("#(?!\\d)\\w*")

Port of Martin Ender's Retina answer to save a few bytes and match added test cases.
Not that String#matches always matches the entire String, so no need for ^...$.

Try it here.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy