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\$
13
  • 10
    \$\begingroup\$ Is # really a valid hashtag? \$\endgroup\$
    – Adám
    Jul 18, 2016 at 11:35
  • 4
    \$\begingroup\$ Is #öäü valid? \$\endgroup\$
    – chrki
    Jul 18, 2016 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\$ Jul 18, 2016 at 13:23
  • 3
    \$\begingroup\$ I assume an alphabet means ascii uppercase or lowercase letter? i.e. abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ? \$\endgroup\$
    – Riker
    Jul 18, 2016 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, 2016 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\$
4
  • \$\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, 2016 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\$ Jul 19, 2016 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\$ Jul 19, 2016 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, 2016 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\$
2
  • \$\begingroup\$ You copied Martin's one and edited it, right? \$\endgroup\$
    – user54200
    Jul 19, 2016 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\$ Jul 19, 2016 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\$
0
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\$
6
  • \$\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, 2016 at 14:42
  • \$\begingroup\$ Yeah I thought about that, thanks for clarifying it! \$\endgroup\$ Jul 19, 2016 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, 2016 at 14:57
  • \$\begingroup\$ Thanks, I rewrote it to handle the case you wrote and made it a lambda function. \$\endgroup\$ Jul 19, 2016 at 15:00
  • 2
    \$\begingroup\$ How about re.compile('#(?!\d)\w*$').match? It’s acceptable to drop the f=, BTW. \$\endgroup\$
    – Lynn
    Jul 19, 2016 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
  • 1
    \$\begingroup\$ +1. Nice! I completely forgot the isalpha() method on my py3 answer :D "#" being true, also destroyed me. \$\endgroup\$
    – Yytsi
    Jul 18, 2016 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\$
2
  • \$\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, 2016 at 17:21
  • \$\begingroup\$ All righty, I can live with that. Have your +1 \$\endgroup\$
    – Value Ink
    Jul 18, 2016 at 20:39
3
\$\begingroup\$

Dyalog APL, 22 20 bytes

Without RegEx:

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

-2 thanks to ngn

\$\endgroup\$
2
  • 1
    \$\begingroup\$ Oh, wow. There are still people who know APL. It's 37 years since I used it! \$\endgroup\$
    – Auspex
    Jul 19, 2016 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, 2016 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\$
1
  • \$\begingroup\$ Nice answer, and welcome to the site! \$\endgroup\$
    – DJMcMayhem
    Jul 18, 2016 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\$
10
  • \$\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, 2016 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, 2016 at 15:37
  • \$\begingroup\$ @LeakyNun Just plain false. \$\endgroup\$
    – Yytsi
    Jul 18, 2016 at 15:47
  • \$\begingroup\$ @LeakyNun Throws IndexOutOfRange for "#" and False for "#d". \$\endgroup\$
    – Yytsi
    Jul 18, 2016 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, 2016 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\$
9
  • \$\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, 2016 at 20:46
  • \$\begingroup\$ No problem, glad I could help! \$\endgroup\$
    – DJMcMayhem
    Jul 18, 2016 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\$ Jul 18, 2016 at 21:23
  • \$\begingroup\$ @TheBikingViking sorry, I will try to fix this now \$\endgroup\$ Jul 18, 2016 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\$
    – user45941
    Jul 18, 2016 at 22:44
1
\$\begingroup\$

Pyke, 19 bytes

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

Try it here!

Quick fix for tonight

\$\endgroup\$
3
  • 1
    \$\begingroup\$ @kenorb rebooted it, ping me if any more issues \$\endgroup\$
    – Blue
    Jul 18, 2016 at 22:15
  • \$\begingroup\$ #123 returns still nothing, shouldn't return 0? \$\endgroup\$
    – kenorb
    Jul 18, 2016 at 22:25
  • 1
    \$\begingroup\$ Nothing is a boolean false \$\endgroup\$
    – Blue
    Jul 18, 2016 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
  • 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, 2016 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\$
6
  • 4
    \$\begingroup\$ isAlphaNum$orelse that's rather threatening... \$\endgroup\$
    – cat
    Jul 18, 2016 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, 2016 at 17:38
  • 2
    \$\begingroup\$ It's like, AlphaNum, orelse!! (orelse what?) \$\endgroup\$
    – cat
    Jul 18, 2016 at 17:43
  • \$\begingroup\$ One might consider the o explode at the end to be quite threatening too ... \$\endgroup\$
    – Laikoni
    Jul 18, 2016 at 17:48
  • 1
    \$\begingroup\$ SML seems quite scary, I don't think I could handle that all day :c \$\endgroup\$
    – cat
    Jul 18, 2016 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\$
3
  • \$\begingroup\$ I think this won't match a # on its own. \$\endgroup\$ Jul 19, 2016 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\$ Jul 20, 2016 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\$ Jul 20, 2016 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\$
1
  • \$\begingroup\$ Whoops, this actually gives a NPE for "#". Give me a sec. \$\endgroup\$ Nov 29, 2016 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\$
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.