25
\$\begingroup\$

If you've ever tried adding labels to a really dense plot, then you'll realise that sometimes labels will overlap one another, making them hard to read. We're going to do something similar but in 1D.

Input will be a sequence of (label, x-coordinate) pairs, and output will be the result of drawing each point and label, in the given order. An asterisk * representing the point should be placed at the given x-coordinate and the label should follow. Any existing characters will be overwritten.

For instance, if the input was

Hello  0
World  8
Fizz   3
Buzz   5
PPCG   16
X      9

Then the following would happen:

*Hello
*Hello  *World
*He*Fizz*World
*He*F*Buzzorld
*He*F*Buzzorld  *PPCG  
*He*F*Buz*Xrld  *PPCG

The final line should then be outputted.

I/O rules

  • Input may consist of any number of pairs. Each label will only consist of uppercase and lowercase letters, and label lengths will be at most 127 chars. Each x-coordinate will be between 0 and 127 inclusive.

  • Input may be in any convenient list or string format such that the pairs are unambiguous and labels/x-coordinates alternate in the input. For example, a format like [("Hello", 0), ("World", 8) ...] or [0 "Hello" 8 "World" ...] is fine. However, you may not assume two separate lists of labels and x-coordinates.

  • Functions and full programs are both okay.

  • Any spots not covered by a label should be represented with a space. However, there may not be any extraneous leading or trailing whitespace aside from a single optional trailing newline.

Examples

Input:

OneLabel   10

Output:

          *OneLabel

Input:

Heathrow   0
Edinburgh  2
London     4
Liverpool  6
Oxford     8

Output:

*H*E*L*L*Oxfordl

Input:

alpha     20
beta       4
gamma     57
delta      3
epsilon   22
zeta      32
eta       53
theta     27

Output:

   *delta           *a*epsi*thetazeta                *eta*gamma

Input:

abc  5
d    5
abc  10
ABCDEFGHIJKLMNOPQRSTUVWXYZ 127

Output:

     *dbc *abc                                                                                                                 *ABCDEFGHIJKLMNOPQRSTUVWXYZ

Note that labels and/or x-coordinates may be repeated.

\$\endgroup\$
2
  • \$\begingroup\$ If the x coords are [0,127] and the strings are (0,127], can a label run off the far right end of the line, or is it protected? That is, does "foo 127" end the line with "*" or "*foo"? Just checking if the string should have a soft or hard ending. \$\endgroup\$ Oct 19 '15 at 13:25
  • 3
    \$\begingroup\$ @PotatoOmeletteSandwich My intention was so that the total length fits within 255, so the maximal output length will occur when there is a length 127 label at x-coordinate 127. The final output should not be truncated in any way, except to remove trailing whitespace. \$\endgroup\$
    – Sp3000
    Oct 19 '15 at 13:28

16 Answers 16

7
\$\begingroup\$

CJam, 24 23 19 bytes

l~Sf.*'*f*:.{S^+1=}

This reads the input as a CJam array of coordinate-label pairs.

Try this fiddle in the CJam interpreter or verify all test cases at once.

Thanks to @MartinBüttner for helping me save 4 bytes!

How it works

l~                   Read a line from STDIN and evaluate it.
  Sf                 For each pair, push the pair and " "; then:
    .*                 Perform vectorized repetition.
                         [X "label"] " " .* -> [(X spaces) "label"]
      '*f*           Join each resulting pair, using '*' as separator.
          :.{     }  Reduce by the following vectorized operator:
                       Push two characters (A and B).
             S^        Compute the symmetric difference of B and " ".
                       This pushes "B " for a non-space B and "" otherwise.
                +1=    Append and select the second character (with wrap).
                       This selects B for "AB " and A for "A".
\$\endgroup\$
1
  • 2
    \$\begingroup\$ I just added a test case and thought I'd leave a comment saying that no, it didn't break this submission - CJam's interpreter output just word wraps. Just in case anybody gets confused. \$\endgroup\$
    – Sp3000
    Oct 19 '15 at 13:37
5
\$\begingroup\$

Python 2, 67 bytes

z=''
for a,b in input():z=(z+' '*b)[:b]+'*'+a+z[len(a)-~b:]
print z

Takes input like [('Heathrow', 0), ('Edinburgh', 2), ('London', 4), ('Liverpool', 6), ('Oxford', 8)] and prints the result.

Python doesn't allow strings to be modified, and converting to and from a list is expensive. So, this recreates the string z to add in a new word. We take the b characters before the word, padding with spaces if needed, then the new text with an asterisk, then the part of z after the new word. Note that trailing spaces are never added.

The reduce version is 3 chars longer (70):

lambda I:reduce(lambda z,(a,b):(z+' '*b)[:b]+'*'+a+z[len(a)-~b:],I,"")
\$\endgroup\$
4
\$\begingroup\$

Pyth, 20 bytes

V.Tmrj" *"d9Qpe+d-Nd

Try it online: Demonstration or Test Suite

Explanation

V.Tmrj" *"d9Qpe+d-Nd
   m        Q         map each pair d of the input to:
     j" *"d             join d by the string " *"
    r      9            range-length encode 
                        (this gives x-coordinate spaces, a star and the label)
 .T                   transpose this table 
V                     for N in ^:
                 -Nd    remove spaces from N
               +d       add a space at the beginning
              e         take the last character
             p          and print it (without newline)
\$\endgroup\$
1
  • 1
    \$\begingroup\$ This is way better than what I had. \$\endgroup\$
    – isaacg
    Oct 19 '15 at 20:43
4
\$\begingroup\$

JavaScript ES6, 104 bytes

c=>(a=Array(255).fill(" "))&&c.map(([u,v])=>a.splice(u,v.length+1,..."*"+v))&&a.join``.replace(/ +$/,"")

Example Usage

Input into a compliant console:

t = [[0,"Hello"],[8,"World"],[3,"Fizz"],[5,"Buzz"],[16,"PPCG"],[9,"X"]];
(c=>(a=Array(255).fill(" "))&&c.map(([u,v])=>a.splice(u,v.length+1,..."*"+v))&&a.join``.replace(/ +$/,""))(t);

Output from the last statement:

"*He*F*Buz*Xrld  *PPCG"

Explanation

This creates an anonymous function from c to three expressions that have been logically-ANDed together. The first two statements are always truthy, and the JS short-circuit rules say that whenever the first is truthy, return the entire value on the right hand side (without coercing to boolean): so this is formally equivalent to

(function (c) {
    a = Array(255).fill(" ");                    // global variable `a` overwritten
    c.map(function (x) {                         // only side-effects are used here.
       var u = x[0], v = x[1];                   // ES6 destructuring
       a.splice(u, v.length + 1, ..."*" + v));   // main logic
    });
    return a.join("").replace(/ +$/, "");        // postprocessing and trim
})

The first statement has to be wrapped in parentheses above because the assignment operator = has lower precedence than the logical-AND operator &&.

The "rest parameter" assembly ..."*"+v is also part of ES6; it concatenates a leading * to the string and then interprets it as a list-like parameter, splitting it into a bunch of arguments which are provided to Array.prototype.splice, which takes (m, n, ...rest) and modifies its array at position m to remove n elements and then insert all of the rest arguments. To accomplish this before ES6 you would use the more cumbersome:

[].slice.apply(a, [u, v.length + 1].concat(("*" + v).split("")))

The array then gets concatenated with the empty string and trailing whitespace is deleted.

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

Ruby, 94 81 75 bytes

Golfed:

s=" "*128;$<.map{|l|w,p=l.split;p=p.to_i;s[p..w.size+p]="*"+w};$><<s.rstrip

Here's the ungolfed code:

s = " "*128
$<.map{|l|                 # for each line entered via stdin, ctrl+D to stop
  w,p = l.split            # had to move the chomp down here
  p = p.to_i               # there's no 'to_i!'...
  s[p..w.size+p] = "*"+w   # in the range of *foobar, replace the string
}
$><<s.rstrip               # output suggested by w0lf

Thanks @w0lf for the suggestions on mapping the input!

Thanks @w0lf and @Not that Charles for the thought on removing a variable.

\$\endgroup\$
9
  • \$\begingroup\$ See the Ruby golfing tips. In this case you could apply the $<.map{|l|...} is shorter than while l=gets;...;end tip and probably replace puts with $><< (which does not require the extra space). \$\endgroup\$ Oct 19 '15 at 14:25
  • \$\begingroup\$ also, I think .chomp can be removed. \$\endgroup\$ Oct 19 '15 at 14:32
  • \$\begingroup\$ In this case, now that you mention it, I think it's likely very safe to remove, as .to_i will catch that. Good thought. Thanks @w0lf! \$\endgroup\$ Oct 19 '15 at 14:34
  • \$\begingroup\$ You're welcome! Here's a shorter version in which I applied the tips above and a few more: ideone.com/BiOvV5. Feel free to post it in your answer if you like it. \$\endgroup\$ Oct 19 '15 at 14:38
  • 3
    \$\begingroup\$ @PotatoOmeletteSandwich Upgrade your Ruby. 1.8.7 is end-of-lifed! also, you should be able to use the s[int, int] form instead of s[range] for 1 char savings. \$\endgroup\$ Oct 19 '15 at 16:04
3
\$\begingroup\$

Javascript 121 chars

Using non-standard features, works on Firefox.
x=Array(255).fill(" ");eval(prompt()).map(s=>{s[0].split``.map((n,i)=>x[s[1]+i+1]=n);x[s[1]]="*"});x=x.join``.trimRight()

Older version: x=Array(255).fill(" ");eval(prompt()).map(s=>{s[0].split``.map((n,i)=>x[s[1]+i+1]=n);x[s[1]]="*"});x=x.join``.replace(/ +$/,"")

x=Array(255).fill(" ");      //Creates an array with spaces
eval(prompt())               //Gets some input, has to look like [["Hello",4],["Hi",14],["Oi",0]]
.map(s=>{s[0].split``.map((n,i)=>x[s[1]+i+1]=n);x[s[1]]="*"}); //Main "logic"
x=x.join``.replace(/ +$/,"") //Gets rid of the trailing spaces
\$\endgroup\$
3
  • 1
    \$\begingroup\$ / +/ makes much more sense than \s disappointed I missed that! Could you save bytes using x=' '.repeat(255); and avoiding the .join? \$\endgroup\$ Oct 19 '15 at 19:21
  • 1
    \$\begingroup\$ @DomHastings: JS strings are immutable so you would have to .split('') it into a mutable data structure, but at that point Array(255).fill(' ') is shorter. In my version most of my savings come from (a) using the "you can give either a function or program" rule to delete eval(prompt()) in exchange for c=> and (b) using the builtin Array.prototype.slice method with a rest parameter to shorten the logic part a little bit. \$\endgroup\$
    – CR Drost
    Oct 19 '15 at 20:07
  • 1
    \$\begingroup\$ @ChrisDrost ah of course... I forget it's just an accessor! Shame[].map.call(s[0], doesn't save any either... \$\endgroup\$ Oct 19 '15 at 20:24
3
\$\begingroup\$

Jelly, 14 bytes

0x;”*;µ/€Ṛo/o⁶

Try it online!

Takes input as a list of pairs [coordinate, label]. The last test case does work, TIO simply has wrapping

How it works

0x;”*;µ/€Ṛo/o⁶ - Main link. Takes a list of pairs I
        €      - Over each pair P in I:
      µ        -   Run the following, using:
       /       -     C (coord) as the left argument and L (label) as the right:
0x             -       Repeat 0 C times
  ;”*          -       Concatenate "*"
     ;         -       Append L
         Ṛ     - Reverse I
          o/   - Reduce columnwise by logical OR
                 This replaces 0s with the next non-zero element
            o⁶ - Replace the remaining zeros with spaces
\$\endgroup\$
2
\$\begingroup\$

Python, 85 bytes

def g(p):
 z=[' ']*256
 for a,b in p:z[b:b+len(a)+1]='*'+a
 return''.join(z).rstrip()

Try it online

\$\endgroup\$
3
  • 1
    \$\begingroup\$ You should be able to do 'z'[2::5] (backticks instead of apostrophes) instead of ''.join(z) to save one byte, and moving z=[' ']*256 to the parameters should save another. Also, I think you can change return to print. \$\endgroup\$
    – Kade
    Oct 19 '15 at 15:59
  • \$\begingroup\$ I think you can save chars by writing a program with p=input() (Python 2) rather than a function, which avoids indentation. Also, b+len(a)+1 can be b-~len(a) \$\endgroup\$
    – xnor
    Oct 19 '15 at 19:27
  • 1
    \$\begingroup\$ Actually, a program just lets you do for a,b in input():. \$\endgroup\$
    – xnor
    Oct 19 '15 at 19:41
2
\$\begingroup\$

Perl, 66 bytes

63 bytes script + 3 bytes for -p

$}||=$"x128;/\s+/,substr$},$',1+length$`,"*$`"}{$_=$};s/\s+$/
/

Nothing too special, utilising the variables $` and $' which are 'before the match' and 'after the match' respectively, instead of splitting the string. I used a $} for the string variable as originally it was saving me a byte, but isn't any more!

Example run:

$perl -p overwritlabels.pl <<< 'Hello  0
World  8
Fizz   3
Buzz   5
PPCG   16
X      9'
*He*F*Buz*Xrld  *PPCG

Perl, 65 bytes

62 bytes script + 3 bytes for -p

Another version that prints out each line (for one less byte!). (Yes, I made this because I didn't read the question properly...)

$}||=$"x128;/\s+/;substr$},$',1+length$`,"*$`";$_=$};s/\s+$/
/

Example run:

$perl -p overwritlabels.pl <<< 'Hello  0
World  8
Fizz   3
Buzz   5
PPCG   16
X      9'
*Hello
*Hello  *World
*He*Fizz*World
*He*F*Buzzorld
*He*F*Buzzorld  *PPCG
*He*F*Buz*Xrld  *PPCG
\$\endgroup\$
0
2
\$\begingroup\$

PHP -- 84 bytes

<? foreach(array_chunk(array_slice($argv,1),2) as $p) echo "␣[".($p[1]+1)."G*$p[0]";
                                                            ^ ESC character (\x1b)

Uses ANSI escape codes to position the cursor (\x1b[XG, featuring the Escape character and X being the 1-based coordinate), followed by the * then the input string for that row. Accepts input on the command line of the form:

php filename.php Heathrow 0 Edinburgh 2 London 4 Liverpool 6 Oxford 8
php filename.php abc 5 d 5 abc 10 ABCDEFGHIJKLMNOPQRSTUVWXYZ 127

Accepts multi-word entries if they're in quotes, since they're command line arguments.

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

C++11, 95 bytes

Why not?

As a function, receive input as a map<int, string> named v containing the position and the string.

string t(255,' ');for(auto&m:v){int i=m.first;t[i++]='*';for(auto&c:m.second)t[i++]=c;}cout<<t;

Usage

#include <iostream>
#include <map>
using namespace std;
int main(){
    map<int,string> v{{0,"Heathrow"},{2,"Edinburgh"},{4,"London"},{6,"Liverpool"},{8,"Oxford"}};
    string t(255,' ');for(auto&m:v){int i=m.first;t[i++]='*';for(auto&c:m.second)t[i++]=c;}cout<<t;
}

Check it Running Here

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

Husk, 13 bytes

Fż|mF(J'*eR' 

Try it online!

Takes a list of pairs (coordinate, label) as input. N.B.: there is a space character at the end of the code.

Explanation

Fż|mF(J'*eR' 
   m             For each pair in the input
    F            apply the following function using the pair members as arguments
          R'     Repeat a space (coord) time
         e       put this in a list with the label
      J'*        and join the strings in the list with "*"
F                Fold over the resulting list
 ż               by zipping strings together 
                   (keeping extra characters in the longer string unchanged) 
  |              with OR (Second argument if truthy else first. Spaces in Husk are falsy)
\$\endgroup\$
1
\$\begingroup\$

Japt, 18 bytes

rÈh'*+YÎYÌ}Sp#ÿ)x1

Try it online!

Takes input as an array of [label, x-coordinate]

Explanation:

rÈh'*+YÎYÌ}Sp#ÿ)x1    #
           Sp#ÿ       # Store 255 spaces as X
r         }    )      # Do this for each pair in order:
 Èh                   #  Overwrite part of X:
        YÌ            #   Starting at index equal to the second item of the pair
   '*                 #   Write "*"
     +YÎ              #   Write the first item of the pair
                x1    # Remove any remaining whitespace on the right side
\$\endgroup\$
0
\$\begingroup\$

Canvas, 9 bytes

H{}0┘*;+╋

Try it here!

Same as my Stax answer, except canvas is better at grid overlaps.

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

GolfScript, 29 bytes

~""\{~." "*3$\+<"*"+\+.,@>+}/

Try it online!

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

Stax, 11 8 bytes

┤6╧ß╡⌠░$

Run and debug it

Takes input as a list of pairs [coordinate, label]. Thanks to caird coinheringaahing for the testcases.

Explanation

zsFE'*s+|%
zs            push empty list and swap with the input
  F           for each element,
   E          push each item onto stack
    '*s+      add asterisk to label
              Stack: [index,label]
        |%    embed in the array
\$\endgroup\$

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.