1
\$\begingroup\$

I have an array of values containing names, e.g.

let members = [{id: 1, name: 'Tyler'}, {id: 2, name: 'John'}, {id: 3, name: 'Dayo'}, ..., {id: 7, name: 'George']

There's multiple ways I can generate a string that looks like:

Tyler, John, Dayo and 4 Others

Here's one way I'm doing this:

let memberString = members.length <= 3 ? 
    members.map(member => member.name).join(', ')
    :
    members.slice(0,3).map(member => member.name).join(', ') + ' and ' + (members.length-3) + ' others'

Using map is the most common way. I've been trying to search similar question on stack overflow and other sites to see what's the smartest way possible to achieve this but couldn't find any. Looking for experts to leave their ways to achieve this.

\$\endgroup\$

2 Answers 2

2
\$\begingroup\$

There are of course many ways to achieve this, but the shortest possible expression is not necessarily going survive any sort of refactoring to cope with edge cases. It's generally better for code to be expressive.

The first step to that is to encapsulate the functionality into a suitably named function. This allows the calling code to clearly state what it expects to happen. How verbose you need to be depends on the application complexity.

Your quoted example code does the same .map().join() in two places, which is a bit redundant (and is likely why you desire to shorten/simplify it). Given that we only need the name from the object, we can do the map first.

reduce() is then perhaps a better choice for converting the array elements to the components of the string. This is arguably a bit inefficient because it's going to iterate over additional members of the array because we can't tell reduce to bail out.

Finally we can join all the elements with the comma, which also gives us an Oxford comma. Note that this is not going to be correct for edge cases such as 0 or 1 names, different counts. And that's why the shortest possible expression is probably not what you need.

let members = [{id: 1, name: 'Tyler'}, {id: 2, name: 'John'}, {id: 3, name: 'Dayo'}, {id: 7, name: 'George'}]

function stringifyMembers(members, count = members.length) {
    return members.map(member => member.name).reduce((acc, name, index) => {
        return index < count
            ? [ ...acc, name ]
            : index === count && members.length > count
                ? [ ...acc, `and ${members.length - count} others` ]
                : acc
    }, []).join(', ')
}

console.log(stringifyMembers(members, 3))

\$\endgroup\$
2
  • \$\begingroup\$ Thank you sir for the detailed answer. This really was helpful. My example basically is a named function I just removed the function delimiters for this question which I believe I shouldn't when asking for code reviews. Thank you for this explanation, this really is helpful. \$\endgroup\$ Jun 23 at 16:09
  • \$\begingroup\$ @Salman, you're right that you should have included full context in your question (this is one aspect where Code Review is very different to Stack Overflow). I hope you take that into account for your next review request! \$\endgroup\$ Jun 24 at 6:59
1
\$\begingroup\$

Whilst you claim your code is the "shortest", the shortest code is rarely the best solution to any programming problem.

The obvious first improvement is to create a function, so it's reusable - pass the list and the projection (e.g. member => member.name) to use.

Then, observe the repeated use of 3 throughout the function. All these 3s refer to the number of elements to specifically mention, so make that clear by using a named constant. In fact, this ought to be another argument to the function.

Finally, when all members are named (i.e. the first branch of the conditional), it would be more user-friendly to use and instead of the last , in the list.

Think about how you are going to make this translatable to non-English languages (hint: constructing messages using + is usually less-translatable than using format strings).

Finally, there's a disagreement between your example, which ends with "Others", and the code, which produces "others".

\$\endgroup\$
1
  • \$\begingroup\$ Reliably shortest I should've asked. Thank you for the corrections though. \$\endgroup\$ Jun 23 at 16:11

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.