DEV Community

dev.to staff
dev.to 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 yo+challenge@dev.to with your suggestions!

Top comments (24)

Collapse
 
willsmart profile image
willsmart

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:

[
  [],
  ["Peter"],
  ["Jacob", "Alex"],
  ["Max", "John", "Mark"],
  ["Alex", "Jacob", "Mark", "Max"],
  ["Alex", "Mark", ...Array(10000)]
].map(likesStringForNames)
-->
[
  "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"
]
Collapse
 
peter279k profile image
peter279k

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);
    }
}
Collapse
 
jpantunes profile image
JP Antunes • Edited

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`;
        default:
            return `${arr[0]}, ${arr[1]} and ${arr.length - 2} others like this`;
    }
}
Collapse
 
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
Collapse
 
aminnairi profile image
Amin • Edited

TypeScript

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.`;
Collapse
 
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",
  };
}
Collapse
 
astagi profile image
Andrea Stagi • Edited

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"]))
Collapse
 
rafaacioly profile image
Rafael Acioly • Edited

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)
Collapse
 
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"
Collapse
 
mxldevs profile image
MxL Devs • Edited

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
  else
    "%s, %s and %d others like this" %[*names[0..1], num_people - 2] 
  end
end

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"
Collapse
 
nombrekeff profile image
Keff

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

function likes(names) {
  switch(names.length){
    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`;
  }
}
Collapse
 
nombrekeff profile image
Keff

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

Collapse
 
celyes profile image
Ilyes Chouia • Edited

The solution in PHP

function likes(array $names): string
{
  $likeWord = "like";
  $string = "";
  switch(count($names)){
    case 0:
      $string = sprintf("No one %s this", $likeWord."s");
      break;
    case 1:
      $string = sprintf("%s %s this", $names[0],$likeWord."s");
      break;
    case 2:
      $string = sprintf("%s and %s %s this", $names[0], $names[1], $likeWord);
      break;
    case 3:
      $string = sprintf("%s, %s and %s %s this", $names[0], $names[1], $names[2], $likeWord);
      break;
    default:
      $string = sprintf("%s, %s and %s others %s this", $names[0], $names[1], count($names) - 2 , $likeWord);
  }
  return $string;
}
Collapse
 
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
     );
 };

Collapse
 
benaryorg profile image
#benaryorg

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: play.rust-lang.org/?version=stable...

Collapse
 
savagepixie profile image
SavagePixie

Elixir

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
  end

  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"
end
Collapse
 
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.`;
}

Some comments may only be visible to logged-in visitors. Sign in to view all comments.