Abbreviate list of names until they fit

7

1

Given a list (array) of names, your challenge is to join the items using some glue and abbreviate the longest items first until the joined string fits a maximum length. And output that string. Since the glue is part of the final string, the length of the glue is included too

Abbreviation is done using the character

Examples

For example when | is the glue and 20 is the maximum length

  • giraffe would become giraffe (length = 7)
  • giraffe, Spiny lumpsucker would become giraffe|Spiny lumps… (length = 20)
  • giraffe, Spiny lumpsucker, rhinoceros would become giraf…|Spiny…|rhino… (length = 20)
  • giraffe, Spiny lumpsucker, rhinoceros, Hellbender would become any of these:
    • gira…|Spi…|rhi…|Hel…
    • gir…|Spin…|rhi…|Hel…
    • gir…|Spi…|rhin…|Hel…
    • gir…|Spi…|rhi…|Hell… (all length = 20)

Rules

The list, glue and maximum length are input parameters of the program.

When abbreviation is needed, abbreviation must be done on any of the longest items first. When the longest items are equally long, it doesn't matter which you abbreviate.

The output length is smaller than the maximum length when no abbreviation was necessary. When abbreviation needs to occur, the length of the output is exactly equal to the maximum length.

The order of the (abbreviated) items must match the order of the items in the input

You can't assume that the abbreviation character and the glue won't occur as characters in the items in the list.

Update

I'm afraid that I overlooked the fact that the length of the glue concatenated with many items by itself may result into a string that is longer than the allowed maximum length. Without considering this, your program may run into an infinite loop. I prefer your program to throw an error in this case. I hope that's not too much of an inconvenience for the ones who already wrote an answer or that are busy writing one. To throw an error is NOT a requirement to win, but I would appreciate if you add a variant that does.

The winner

This is code-golf, so the shortest valid answer – measured in bytes – wins.

Christiaan Westerbeek

Posted 2017-07-11T12:28:48.690

Reputation: 863

Do we choose the glue, or is one given to us in input? – Skidsdev – 2017-07-11T12:33:09.573

@Mayube the glue-string itself is input too – Christiaan Westerbeek – 2017-07-11T12:36:34.453

This is quite an interesting, 'planning to do it – V. Courtois – 2017-07-11T12:51:40.673

1@ChristiaanWesterbeek can we abbreviate more than necessary? I mean, can we output gir…|Spi…|rhi…|Hel… in your example 4? – V. Courtois – 2017-07-11T13:16:26.650

1@V.Courtois No. "When abbreviation needs to occur, the length of the output is exactly equal to the maximum length.". In your comment the output length is 19. It has to be 20 when that's the maximum length – Christiaan Westerbeek – 2017-07-11T13:17:31.897

@ChristiaanWesterbeek Thanks for clarifying. – V. Courtois – 2017-07-11T13:18:31.000

Can we assume that the glue will never be in any of the array strings? – Erik the Outgolfer – 2017-07-11T13:34:05.470

Can we assume that won't be in any string? – Erik the Outgolfer – 2017-07-11T13:40:27.620

To be clear would gir…|Spin…|rhi…|Hel…, gir…|Spi…|rhin…|Hel…, and gir…|Spi…|rhi…|Hell… complete the list of valid outputs in your last example? – Jonathan Allan – 2017-07-11T13:51:42.207

@EriktheOutgolfer You can't assume that. For any means practical your glue and abbreviation characters may occur as values in the list when that list originates from a database fed by some other program that isn't doing proper input validation. – Christiaan Westerbeek – 2017-07-11T14:00:09.233

@JonathanAllan Yes – Christiaan Westerbeek – 2017-07-11T14:00:39.400

Answers

5

Jelly, 25 bytes

ṖṖ;⁽÷ẓỌµ€µL€MḢµ¦µj⁴L>⁵ð¿j

Try it online!

This unfortunately does not contain the error-if-invalid feature.

Explanation

This is what I call the Jelly syndrome: the easy part is explaining the algorithm, the hard part is explaining how you found the exact order to put the code in to make it execute correctly.

ṖṖ;⁽÷ẓỌµ€µL€MḢµ¦µj⁴L>⁵ð¿j
                       ¿     While
                 j           joining the input list
                  ⁴          with the glue produces a string
                   L         whose length
                    >        is larger than
                     ⁵       the maximum,
               ¦             in the input array at the
             Ḣ               first of the positions that
            M                have maximal
          L€                 length,
ṖṖ                           remove the last two characters,
  ;                          append
   ⁽÷ẓ                       the number 8230
      Ọ                      and cast it to a character.
                        j    Finally, join again with the glue.

PurkkaKoodari

Posted 2017-07-11T12:28:48.690

Reputation: 16 699

It's a draw with the 05AB1E answer. What now? – Christiaan Westerbeek – 2017-07-14T20:21:39.067

@ChristiaanWesterbeek Whoever got that score first wins. I apparently posted my 25 character solution 3 minutes before Erik posted his.

– PurkkaKoodari – 2017-07-14T21:44:26.100

3

05AB1E, 25 bytes

[гýDg²›≠#\Déθ©k®¨¨"…"«sǝ

Try it online!

Erik the Outgolfer

Posted 2017-07-11T12:28:48.690

Reputation: 38 134

It's a draw with the Jelly answer. What now? – Christiaan Westerbeek – 2017-07-14T20:21:16.060

@ChristiaanWesterbeek Jelly was first so it wins (look at revision histories not post dates). – Erik the Outgolfer – 2017-07-15T11:12:43.460

3

Python 3, 102 bytes

def f(l,n,g):
 while len(g.join(l))>n:i=l.index(max(l,key=len));l[i]=l[i][:-2]+'…'
 return g.join(l)

Try it online!

Alternatively if you want to see the result being shortened over time:

def f(l,n,g):
 while len(g.join(l))>n:i=l.index(max(l,key=len));l[i]=l[i][:-2]+'…';yield g.join(l)

wrymug

Posted 2017-07-11T12:28:48.690

Reputation: 772

3

JavaScript (ES6), 153 152 bytes

(a,g,n)=>eval(`while(a.join('')#>n-2*a#+1+a#==1){for(i=n;x=a.findIndex(w=>w#==i),i--&&!~x;);a[x]=a[x].slice(0,-2)+'…'}a.join(g)`.split`#`.join`.length`)

f=(a,g,n)=>eval(`while(a.join('')#>n-2*a#+1+a#==1){for(i=n;x=a.findIndex(w=>w#==i),i--&&!~x;);a[x]=a[x].slice(0,-2)+'…'}a.join(g)`.split`#`.join`.length`)
console.log(
  f(['giraffe'], '|', 7),
  f('giraffe, Spiny lumpsucker'.split`, `, '|', 20),
  f('giraffe, Spiny lumpsucker, rhinoceros'.split`, `, '|', 20),
  f('giraffe, Spiny lumpsucker, rhinoceros, Hellbender'.split`, `, '|', 20)
)

darrylyeo

Posted 2017-07-11T12:28:48.690

Reputation: 6 214

1

JavaScript (ES6), 91 bytes

a=>n=>g=>f=(l=n,r=a.join(g))=>r[n]?f(l-!a.some((s,i)=>s[l]&&(a[i]=s.slice(0,-2)+'…'))):r

Uses currying i.e. F(A)(N)(G)(). Explanation: No string can have a length of more than n and still fit, so without loss of generality the overlong length l starts off at n. Each recursive call then checks whether the strings fit. If not, then the first overlong string found is trimmed, otherwise the overlong length is decremented when all strings have been trimmed to that length.

Neil

Posted 2017-07-11T12:28:48.690

Reputation: 95 035

Cool. This is how I used it x=a=>n=>g=>f=(l=n,r=a.join(g))=>r[n]?f(l-!a.some((s,i)=>s[l]&&(a[i]=s.slice(0,-2)+'…'))):r and than x(['giraffe', 'Spiny lumpsucker'])(20)('|')(). Works. But x(['giraffe', 'Spiny lumpsucker'])(2)('|')() gives a Maximum call stack size error (because it wouldn't all fit in a string with a length of 2). But that's something I can catch. – Christiaan Westerbeek – 2017-07-14T19:25:05.637