regexextract can return an array, you'd need to know how many there are, so this isn't useful in your case. The way to get around this limitation of
regexextract is to use
regexreplace to get rid of the text you don't want. Like this:
=trim(regexreplace(A2, "(^|\s)[^#]\S*", ""))
The regular expression matches the words not beginning with # and replaces them with empty string. The trim function cleans up possible extra spaces that remain.
The above works fine for your examples, but has some defects in more complex situations:
Breaking #news-in just now! A new photo #contest! Code name #as_shot (important underscore here). #something.
The above command returns
#news-in #contest! #as_shot #something.
not realizing that hashtags cannot include
-!. (they are limited to "word characters", A-Za-z0-9_). So, another regexreplace is needed to clean up. I put both in one command: such nesting of
regexreplace is pretty common.
=regexreplace(trim(regexreplace(A2, "(^|\s)[^#]\S*","")), "[^#\w\s]\S*", "")
The outer replacement removes the parts of hashtags from the first character that's neither
\w nor # to the end. The output has correct hashtags:
#news #contest #as_shot #something
It seems that both
regexreplace commands can be rolled into one, by giving it both of the above regexes as alternatives. The logic of this combined regex is harder to follow, but it has worked in all examples I tried.
=trim(regexreplace(A2, "((^|\s)[^#]\S*)|([^#\w\s]\S*)", ""))