DEV Community staff staff

Posted on

Daily Challenge #266 - Who Likes It?

You probably know the "like" system from Facebook and other pages. People can "like" blog posts, pictures or other items. We want to create the text that should be displayed next to such an item.

Implement a function likes :: [String] -> String, which must take in input array, containing the names of people who like an item. It must return the display text as shown in these examples:

likes {} // must be "no one likes this"
likes {"Peter"} // must be "Peter likes this"
likes {"Jacob", "Alex"} // must be "Jacob and Alex like this"
likes {"Max", "John", "Mark"} // must be "Max, John and Mark like this"
likes {"Alex", "Jacob", "Mark", "Max"} // must be "Alex, Jacob and 2 others like this"

For 4 or more names, the number in and 2 others simply increases.

Good luck!

This challenge comes from BattleRattle on CodeWars. Thank you to CodeWars, who has licensed redistribution of this challenge under the 2-Clause BSD License!

Want to propose a challenge idea for a future post? Email with your suggestions!

Discussion (27)

willsmart profile image

This'd be a good use for template literals. Here's a quicky in javascript:

function likesStringForNames(names) {
  const [first, second, third] = names;
  switch (names.length) {
    case 0: return "no one likes this";
    case 1: return `${first} likes this`;
    case 2: return `${first} and ${second} like this`;
    case 3: return `${first}, ${second} and ${third} like this`;
    default: return `${first}, ${second} and ${names.length - 2} others like this`;

Sanity check:

  ["Jacob", "Alex"],
  ["Max", "John", "Mark"],
  ["Alex", "Jacob", "Mark", "Max"],
  ["Alex", "Mark", ...Array(10000)]
  "no one likes this",
  "Peter likes this",
  "Jacob and Alex like this",
  "Max, John and Mark like this",
  "Alex, Jacob and 2 others like this",
  "Alex, Mark and 10000 others like this"
peter279k profile image

Here is the code snippets with PHP:

function likes( $names ) {
    $namesCount = count($names);
    if ($namesCount === 0) {
        return "no one likes this";

    if ($namesCount === 1) {
        return sprintf("%s likes this", $names[0]);

    if ($namesCount === 2) {
        return sprintf("%s and %s like this", $names[0], $names[1]);

    if ($namesCount === 3) {
        return sprintf("%s, %s and %s like this", $names[0], $names[1], $names[2]);

    if ($namesCount >= 4) {
        $likesStringFormat = "%s, %s and %d others like this";

        $otherNamesCount = $namesCount - 2;

        return sprintf($likesStringFormat, $names[0], $names[1], $otherNamesCount);
jpantunes profile image
JP Antunes • Edited on

I think you don't need the break after the return, it would be unreachable.

const likes = (arr = []) => {
    switch (arr.length) {
        case 0:
            return "no one likes this";
        case 1:
            return `${arr[0]} likes this`;
        case 2:
            return `${arr[0]} and ${arr[1]} like this`;
        case 3:
            return `${arr[0]}, ${arr[1]} and ${arr[2]} like this`;
            return `${arr[0]}, ${arr[1]} and ${arr.length - 2} others like this`;
dry profile image
Hayden Mankin
let likes = ([a, b, ...c]) => c.length > 1 ? `${a}, ${b} and ${c.length} others like this` : c.length ? `${a}, ${b} and ${c[0]} like this` : b ? `${a} and ${b} like this` : a ? `${a} likes this` : `no one likes this`;

console.log(likes([])); // no one likes this
console.log(likes(["Peter"])); // Peter likes this
console.log(likes(["Jacob", "Alex"])); // Jacob and Alex like this
console.log(likes(["Max", "John", "Mark"])); // Max, John and Mark like this
console.log(likes(["Alex", "Jacob", "Mark", "Max"])); // Alex, Jacob and 2 others like this
aminnairi profile image
Amin • Edited on


const likes = ([a, b, c, ...xs]: string[]): string =>
    a === undefined
        ? "No one likes this."
        : b === undefined
            ? `${a} likes this.`
            :  c === undefined
                ? `${a} and ${b} like this.`
                : xs.length === 0
                    ? `${a}, ${b} and ${c} like this.`
                    : `${a}, ${b} and ${[c, ...xs].length} others like this.`;
sam_ferree profile image
Sam Ferree

C# switch expressions to the rescue!

public static class Format
  public static string LikeText(string[] names) => names.Length switch
    0 => "no one likes this",
    1 => $"{names[0]} likes this",
    2 => $"{names[0]} and {names[1}} like this",
    3 => $"{names[0]}, {names[1]}, and {names[2]} like this",
    _ => $"{names[0]}, {names[1]}, and {names.Length-2} others  like this",
astagi profile image
Andrea Stagi • Edited on

For Python

def who_likes_this(names: list) -> str:
    who = 'no one'
    number_of_likes = len(names)
    if number_of_likes > 0 and number_of_likes < 3 :
        who = ' and '.join(names)
    elif number_of_likes >= 3:
        who = ', '.join(names[:2])
        who += ' and ' + (names[2] if number_of_likes == 3 else f' and {len(names[2:])} others')
    return f'{who} like{"" if number_of_likes > 1 else "s" } this'

Test it:

print (who_likes_this([]))
print (who_likes_this(["Peter"]))
print (who_likes_this(["Jacob", "Alex"]))
print (who_likes_this(["Max", "John", "Mark"]))
print (who_likes_this(["Alex", "Jacob", "Mark", "Max"]))
print (who_likes_this(["Alex", "Jacob", "Mark", "Max", "Peter"]))
rafaacioly profile image
Rafael Acioly • Edited on

Here's my version :)

from typing import List

def likes(names: List[str]) -> str:
  messages = {
    0: 'no one likes this',
    1: '%s likes this',
    2: '%s and %s like this',
    3: '%s, %s and %s like this'

  message = messages.get(len(names))
  if not message:
    return '%s, %s and %d others like this' % (*names[:2], len(names) - 2)

  return message % tuple(names)
cipharius profile image
Valts Liepiņš

Very declarative Haskell solution:

likes :: [String] -> String
likes []     = "no one likes this"
likes [name] = name <> " likes this"
likes names  = showMultiple names <> " like this"

showMultiple :: [String] -> String
showMultiple xs = case xs of
  []          -> ""
  [x]         -> x
  [x,x']      -> x <> " and " <> x'
  [x,x',x'']  -> x <> ", " <> showMultiple [x',x'']
  (x:x':rest) -> x <> ", " <> x' <> " and " <> (show $ length rest) <> " others"
mxldevs profile image
MxL Devs • Edited on

Ruby string-formatting takes an array of arguments, so if my function takes an array of names, then I just need to feed it into some pre-formatted "templates". Also the use of splat-array which I don't completely understand actually.

def likes(names)
  num_people = names.size
  case num_people
  when 0
    "no one likes this"
  when 1
    "%s likes this" %names
  when 2
    "%s and %s like this" %names
  when 3
    "%s, %s and %s like this" %names
    "%s, %s and %d others like this" %[*names[0..1], num_people - 2] 

p likes [] # must be "no one likes this"
p likes ["Peter"] # must be "Peter likes this"
p likes ["Jacob", "Alex"] # must be "Jacob and Alex like this"
p likes ["Max", "John", "Mark"] # must be "Max, John and Mark like this"
p likes ["Alex", "Jacob", "Mark", "Max"] # must be "Alex, Jacob and 2 others like this"
nombrekeff profile image

Cool I did this one 3 years ago over at CodeWars, here is the answer I posted:

function likes(names) {
    case 0:  return `no one likes this`; break;
    case 1:  return `${names[0]} likes this`; break;
    case 2:  return `${names[0]} and ${names[1]} like this`; break;
    case 3:  return `${names[0]}, ${names[1]} and ${names[2]} like this`; break;
    default: return `${names[0]}, ${names[1]} and ${names.length - 2} others like this`;
nombrekeff profile image

Looking at it now,break is not needed, as return breaks out of the switch statement.

celyes profile image
Ilyes Chouia • Edited on

The solution in PHP

function likes(array $names): string
  $likeWord = "like";
  $string = "";
    case 0:
      $string = sprintf("No one %s this", $likeWord."s");
    case 1:
      $string = sprintf("%s %s this", $names[0],$likeWord."s");
    case 2:
      $string = sprintf("%s and %s %s this", $names[0], $names[1], $likeWord);
    case 3:
      $string = sprintf("%s, %s and %s %s this", $names[0], $names[1], $names[2], $likeWord);
      $string = sprintf("%s, %s and %s others %s this", $names[0], $names[1], count($names) - 2 , $likeWord);
  return $string;
moufeed_m profile image
Mofid Jobakji
 const likes = (names) => {
   const map = new Map([
     [0, 'no one likes this'],
     [1, '{name} likes this'],
     [2, '{name} and {name} like this'],
     [3, '{name}, {name} and {name} like this'],
     [4, '{name}, {name} and {n} others like this'],

   return map
   .get(names.length < 4 ? names.length : 4)
     .replace(/{name}|{n}/g, (val) =>
       val === '{name}' ? names.shift() : names.length

benaryorg profile image

Strings in Rust are a bit tedious since you usually want to avoid extra allocations and there's ownership concerns, so this code has a lot more refs than I'd like, and the function signature is a classic, but otherwise I think it's kind of pretty.
What I like most is that there's no -2 for the length, because Rust pattern matching lets me capture the elements into another slice right there.

fn like_text<S: AsRef<str>>(likes: &[S]) -> String
    match likes
        &[] => "no one likes this".to_string(),
        &[ref one] => format!("{} likes this", one.as_ref()),
        &[ref one, ref two] => format!("{} and {} like this", one.as_ref(), two.as_ref()),
        &[ref one, ref two, ref three] => format!("{}, {} and {} like this", one.as_ref(), two.as_ref(), three.as_ref()),
        &[ref one, ref two, ref rest@..] => format!("{}, {} and {} others like this", one.as_ref(), two.as_ref(), rest.len())

fn main()
    let likes = ["foo", "bar", "baz", "quux"];

    println!("{}", like_text(&likes))

Permalink to the Rust Playground:

savagepixie profile image


There's probably a much simpler way to solve it, but hey, I basically know how to do pattern matching and recursion, so there we go:

defmodule Like do
  def who_likes(list) do
    count = Enum.count(list)
    _who_likes(list, count - 2)
    |> _add_likes(count)
    |> IO.puts

  defp _add_likes(x, n) when (n < 2), do: "#{x} likes this"
  defp _add_likes(x, _), do: "#{x} like this"

  defp _who_likes([], _), do: "no one"
  defp _who_likes([ a | [] ], _), do: a
  defp _who_likes([ a, b | [] ], _), do: "#{a} and #{b}"
  defp _who_likes([ a, b, c | [] ], _), do: "#{a}, #{b} and #{c}"
  defp _who_likes([ a, b | _tail], count), do: "#{a}, #{b} and #{count} others"
dimitrilahaye profile image
Dimitri Lahaye

Here is my proposition in JS:

function whoLike(names) {
  const l = names.length;
  if (!l) {
    return 'No one likes this';
  } else if (l === 1) {
    return `${names[0]} likes this.`;
  } else if (l < 4) {
    return `${names.slice(0, l - 1).join(', ')} and ${names[l - 1]} like this.`;
  return  `${names.slice(0, 2).join(', ')} and ${l - 2} others like this.`;
alvaromontoro profile image
Alvaro Montoro

Wasn't this in a previous challenge? I recall building this in HTML+CSS...

rafaacioly profile image
Rafael Acioly

I think so Alvaro, i've also done this in python...

bb4l profile image
djjensen profile image
David Jensen • Edited on

Javascript one-liner.
The cutoff can be changed.

let summarizeNamesAtNumNames = 4;
const likes = (names) =>
    names.length === 0
      ? "no one"
      : names
          .slice(0, summarizeNamesAtNumNames - 1)
            (acc, name, index) =>
              acc +
              (index === 0
                ? ""
                : index ===
                  Math.min(names.length - 1, summarizeNamesAtNumNames - 2)
                ? " and "
                : ", ") +
              (names.length >= summarizeNamesAtNumNames &&
              index === summarizeNamesAtNumNames - 2
                ? names.length - (summarizeNamesAtNumNames - 2) + " others"
                : name),
    "like" + (names.length <= 1 ? "s" : ""),
  ].join(" ");

for (let cut = 2; cut < 7; cut++) {
  summarizeNamesAtNumNames = cut;
  console.log(likes(["Jacob", "Alex"]));
  console.log(likes(["Max", "John", "Mark"]));
  console.log(likes(["Alex", "Jacob", "Mark", "Max"]));
  console.log(likes(["Alex", "Jacob", "Mark", "Max", "Joey", "Joe Joe"]));
    likes(["Alex", "Jacob", "Mark", "Max", "Joey", "Joe Joe", "Shabadoo"])
functional_js profile image
Functional Javascript

This is a case where the switch block is probably the simplest idiom with the least cognitive load.

Having to use Math.min, conditional Regex's, replace callbacks, and array shifts, would be unnecessary complexity.

Good work showing the variants to outline the pros and cons though.

functional_js profile image
Functional Javascript

The exception would be if the more "complex" solution was more performant.

For verification, I ran a perf test on the two funcs, and the switch idiom won out. (see screenshot below).

If another idiom were to beat out the switch idiom, I would swap out the implementation for the fastest idiom that uses the least cycles.
Wrap it in a function, document it, and include a resource link to the performance notes, tests and reasoning of the chosen implementation.

Alt Text

jericho22034104 profile image
Jericho Reyes

The last part is, in my opinion , the best part I've ever seen