DEV Community

dev.to staff
dev.to staff

Posted on

Daily Challenge #48 - Facebook Likes

In today's challenge, you are asked to create a like system from Facebook; a text which displays the names of people who liked an item.

For example:

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"

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


Want to propose a challenge idea for a future post? Email yo+challenge@dev.to with your suggestions!

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

Oldest comments (34)

Collapse
 
chrisachard profile image
Chris Achard

JS, with each case defined. I went for straightforward, though there's probably room to condense this somehow given the repetition:

const likes = names => {
  switch(names.length) {
    case 0:
      return "no one likes this"
    case 1:
      return `${names[0]} likes this`
    case 2:
      return `${names[0]} and ${names[1]} like this`
    case 3:
      return `${names[0]}, ${names[1]} and ${names[2]} like this`
    default:
      return `${names[0]}, ${names[1]} and ${names.length - 2} others like this`
  }
}
Collapse
 
jasman7799 profile image
Jarod Smith

lol great minds think alike

Collapse
 
avalander profile image
Avalander • Edited

Some simple pattern matching with Haskell.

likes :: [String] -> String
likes []        = "no one likes this"
likes [a]       = a ++ " likes this"
likes [a, b]    = a ++ " and " ++ b ++ " like this"
likes [a, b, c] = a ++ ", " ++ b ++ " and " ++ c ++ " like this"
likes (a:b:xs)  = a ++ ", " ++ b ++ " and " ++ (show $ length xs) ++ " others like this"
Collapse
 
hanachin profile image
Seiei Miyagi • Edited

ruby <3

def likes(ls)
  case ls
  in []
    "no one likes this"
  in [a]
    format "%s likes this", a
  in [a, b]
    format "%s and %s like this", a, b
  in [a, b, c]
    format "%s, %s and %s like this", a, b, c
  in [a, b, *rest]
    format "%s, %s and %d others like this", a, b, rest.size
  end
end

p likes([]) # => "no one likes this"
p likes(["Peter"]) # => "Peter likes this"
p likes(["Jacob", "Alex"]) # => "Jacob and Alex like this"
p likes(["Max", "John", "Mark"]) # => "Max, John and Mark like this"
p likes(["Alex", "Jacob", "Mark", "Max"]) # => "Alex, Jacob and 2 others like this"
Collapse
 
ben profile image
Ben Halpern

Nice!

Collapse
 
hanachin profile image
Seiei Miyagi

I can't wait ruby 2.7 release!

git clone git@github.com:ruby/ruby.git
cd ruby
autoconf
./configure optflags="-O0" debugflags="-g3" --prefix="$HOME/.rbenv/versions/master"
make && make install
rbenv global master
Collapse
 
alvaromontoro profile image
Alvaro Montoro

CSS

This is a "don't do this at home" type of solution. Will it work? Yes. Is is worth it? We can all agree that CSS is always worth it (:P), but in this case, it may end up with a ton of unnecessary HTML code.

The idea is to use a ul/ol to represent the list of people, and with counters keep track of the number of likes (aka people in the list). Then with pseudo-elements display the appropriate message or connector.

ul, ol { 
  display: block;
  list-style: none;
  padding-left: 0;
  clear: both;
  height: 40px;
  line-height: 40px;
  counter-reset: people -2;
}

li {
  display: inline-block;
  float: left;
  font-size: 1rem;
  counter-increment: people;
}

/* empty list */
ul:empty::before,
ol:empty::before {
  content: "No one likes this post.";
}

/* one element only */
li:first-child:last-child::after {
  content: " likes this post.";
}

/* separate all names by commas */
li:not(:first-child)::before {
  content: ", ";
}

/* the last name (or from the third one) will end the list */
li:nth-child(n + 3)::before,
li:last-child:not(:first-child)::before {
  content: "\00a0 and ";
}

/* add message for multiple names */
li:nth-child(n+3)::after,
li:last-child::after {
  content: " like this post.";
}

/* from the 4th element forth, they must be hidden */
li:nth-child(n+3):not(:last-child) {
  font-size: 0;
}

/* the last element in a list of 4 or more is special */
li:nth-child(n+4):last-child::before {
  font-size: 1rem;
  content: "\00a0 and " counter(people) " others like this post.";
}
li:nth-child(n+4):last-child {
  font-size: 0;
}
li:nth-child(n+4):last-child::after {
  content: "";
}

And here is a demo on codepen:

Collapse
 
alvaromontoro profile image
Alvaro Montoro

Thanks to this challenge, I learnt that CSS counters don't increment in elements with display: none. Which is nice.

Collapse
 
jitheshkt profile image
Jithesh. KT

Bloody brilliant!

Collapse
 
alvaromontoro profile image
Alvaro Montoro

Also, I'm more of an Oxford-comma type of person, but the challenge didn't have it. (That is my excuse and I'll stick to it)

Collapse
 
roninjosue profile image
Reynaldo Josué Cano Bárcenas • Edited

My code in JS

let peoples = [];

let people_likes = (p) => {
let tam = p.length;
if(tam === 0){
console.log("no one likes this");
}
else {
if(tam < 4){
console.log(${p.join(", ")} likes this);
} else {
console.log(${p.slice(0, 2).join(", ")} and ${tam - 2} others like this);
}
}
};

people_likes(peoples);
peoples.push("Peter");
people_likes(peoples);
peoples.push("Jacob");
people_likes(peoples);
peoples.push("Mark");
people_likes(peoples);
peoples.push("Alex");
people_likes(peoples);
peoples.push("Reynaldo");
people_likes(peoples);

Collapse
 
jasman7799 profile image
Jarod Smith
function likes(people) {
  switch (people.length) {
    case 0:
      return 'no one likes this';
    case 1:
      return `${people[0]} likes this`;
    case 2:
      return `${people[0]} and ${people[1]} like this`;
    case 3:
      return `${people[0]}, ${people[1]} and ${people[2]} like this`;
    default:
      return `${people[0]}, ${people[1]} and ${people.length - 2} others like this`;
  }
}

Thank god for template strings.

Collapse
 
celyes profile image
Ilyes Chouia • Edited

PHP

$likes = ["Alex", "John", "Jeremiah", "David", "Travis"];
$message = "";
switch ($likes){
    case (!isset($likes)):
        $message = "no one likes this";
    break;
    case (sizeof($likes) == 1):
        $message = "$likes[0] likes this";
    break;
    case (sizeof($likes) == 2):
        $message = "$likes[0] and $likes[1] like this";
    break;
    case (sizeof($likes) == 3):
        $message = "$likes[0], $likes[1] and $likes[2] like this";
    break;
    case (sizeof($likes) > 3):
        $message = "$likes[0], $likes[1] and ". ( sizeof($likes) - 2 ) . " others like this";
    break;
    default: 
        $message = "no one likes this";
}
echo $message;
Collapse
 
ynndvn profile image
La blatte

And a bit of golf with the Intl.ListFormat tool!

f=(n)=>(b=n.length<2,a=new Intl.ListFormat`en-GB`,(n.length>3?a.format([n[0],n[1],n.length-2+' others']):a.format(n.length?n:['no one']))+` like${b?'s':''} this`)

Here is the output:

f([])
"no one likes this"
f(["Peter"])
"Peter likes this"
f(["Jacob", "Alex"])
"Jacob and Alex like this"
f(["Max", "John", "Mark"])
"Max, John and Mark like this"
f(["Alex", "Jacob", "Mark", "Max"])
"Alex, Jacob and 2 others like this"

Basically, with a bit of wibbly wobbly trickery, depending on the input length, we either call the format function with ["no one"], the complete input, or the two first elements followed by the remaining quantity of items. Finally, we add (or not) an s to the like, and return the built string!

Collapse
 
andre000 profile image
André Adriano • Edited

Using destructuring with JS

const likeText = (likes = []) => {
    if(!likes.length) return 'no one likes this';

    const [first, second, third, ...rest] = likes
    if(!second) return `${first} likes this`;
    if(!third) return `${first} and ${second} like this`;
    if(!rest.length) return `${first}, ${second} and ${third} like this`;
    return `${first}, ${second} and ${rest.length + 1} others like this`;
}
Collapse
 
rafaacioly profile image
Rafael Acioly • Edited

At first i've used a lot of if statement then i got excited and tried another point

Python version (using as fewer ifs as possible):

def show_likes(persons: List[str]) -> str:
  messages = {
    0: 'no one likes this',
    1: '%s like this',
    2: '%s and %s like this',
    3: '%s, %s and %s like this',
    4: '%s, %s and %d others like this'
  }

  likes_quantity = len(persons)
  content = messages.get(likes_quantity) or messages[4]

  if likes_quantity > 3:
    return content % (persons[0], persons[1], len(persons[2:]))

  return content % (tuple(persons))
Collapse
 
idanarye profile image
Idan Arye

Possible improvements: instead of

content = messages.get(likes_quantity) or messages[4]

You can write:

content = messages.get(likes_quantity, messages[4])

Or - if you want to get fancy:

content = messages[min(likes_quantity, 4)]

Or even:

from collections import defaultdict

messages = defaultdict(
    lambda: '%s, %s and %d others like this',
  {
    0: 'no one likes this',
    1: '%s like this',
    2: '%s and %s like this',
    3: '%s, %s and %s like this',
  })

likes_quantity = len(persons)
content = messages[likes_quantity]

But of course - all of this is redundant, since you already check for likes_quantity > 3. So how about this:

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

    likes_quantity = len(persons)

    if likes_quantity > 3:
        return '%s, %s and %d others like this' % (
            persons[0], persons[1], len(persons[2:]))

    return messages[likes_quantity] % tuple(persons)
Collapse
 
rafaacioly profile image
Rafael Acioly

Nice, it was in fact redundant getting the message content, thanks for sharing :D