loading...

Daily Challenge #82 - English Beggars

thepracticaldev profile image dev.to staff ・1 min read

Given an array of values and a number of beggars, you are supposed to return an array with the sum of what each beggar brings home, assuming they all take regular turns, from the first to the last.

For example: [1,2,3,4,5] for 2 beggars will return a result of [9,6], as the first one takes [1,3,5], the second collects [2,4].

The same array with 3 beggars would have in turn have produced a better outcome for the second beggar: [5,7,3], as they will respectively take [1,4], [2,5] and [3].

Also note that not all beggars have to take the same amount of "offers", meaning that the length of the array is not necessarily a multiple of n; length can be even shorter, in which case the last beggars will, of course, take nothing (0).


This challenge comes from GiacomoSorbi on 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!

Discussion

pic
Editor guide
Collapse
tomf_31 profile image
Tōm
function reduceAmount(arr, amount, index) {
    arr[index % arr.length] += amount;
    return arr;
}

function beggars(amounts, beggarsNumber) {
    const sums = [...Array(beggarsNumber)].map(_ => 0);
    return amounts.reduce(reduceAmount, sums);
}

beggars([1,2,3,4,5], 2) // [9, 6]
Collapse
aminnairi profile image
Amin

I'm not sure as I can't test it out here but I think that this line

const sums = [...Array(beggarsNumber)].map(_ => 0);

Could have been written as

const sums = [...Array(beggarsNumber)].fill(0);

Even if it is a small change, I find it easier to read.

Collapse
tomf_31 profile image
Tōm

Thank you for your response, it can also be changed as :

const sums = Array(10).fill(0);

I forgot this function, and it is really helpful and permits to avoid the hackish code I wrote above.

Thanks!

Collapse
citizen428 profile image
Michael Kohl

F#

let beggars (donations : int list) (count : int) : int list =
    let sums =
        donations
        |> List.indexed
        |> List.groupBy (fun (i, _) -> i % count)
        |> List.map (fun (_, l) -> List.sumBy (snd) l)
    sums @ List.replicate (count - sums.Length) 0

Because I was bored I also did a pointless version using pointfree style in the pipeline:

let flip f a b = f b a

let beggars (donations : int list) (count : int) : int list =
    let sums =
        donations
        |> List.indexed
        |> List.groupBy (fst >> (flip (%) count))
        |> List.map (snd >> List.sumBy (snd))
    sums @ List.replicate (count - sums.Length) 0

Tests:

module DailyChallengeTest

open FsUnit.Xunit
open Xunit
open DailyChallenge

[<Fact>]
let ``2 beggars, 5 donations``() =
    beggars [ 1; 2; 3; 4; 5 ] 2 |> should equal [ 9; 6 ]

[<Fact>]
let ``3 beggars, 5 donations``() =
    beggars [ 1; 2; 3; 4; 5 ] 3 |> should equal [ 5; 7; 3 ]

[<Fact>]
let ``2 beggars, 1 donations``() = 
    beggars [ 1 ] 2 |> should equal [ 1; 0 ]

[<Fact>]
let ``2 beggars, 0 donations``() = 
    beggars [] 2 |> should equal [ 0; 0 ]
Collapse
aminnairi profile image
Amin

Could you remind me what is the concept of point free style? I know I heard it somewhere when playing with Haskell but I can't remember haha!

Collapse
citizen428 profile image
Michael Kohl

Making new functions by composing others without mentioning the actual arguments. In this specific case I did it for the higher-order ones in the pipeline:

(fun (i, _) -> i % count) -> (fst >> (flip (%) count))

or

(fun (_, l) -> List.sumBy (snd) l) -> (snd >> List.sumBy (snd))

One could maybe justify the second one (though it's probably still harder to read than the corresponding lambda for most people), but the first one isn't even shorter than the lambda and I had to implement flip myself since it's not part of F#'s standard library.

Collapse
kvharish profile image
K.V.Harish

My solution in js


const beggarsCollection = (collection, numOfBeggars) => {
  const sum = Array(numOfBeggars).fill(0);
  collection.forEach((value, index) => {
    sum[index % numOfBeggars] += value;
  });
  return sum;
};

Collapse
lorenzota profile image
LorenzoTa

A simple perl solution consuming the array

use strict;
use warnings;

my @values = (1..5);
my @beggars;
$#beggars = $ARGV[0]//1;
my $ind = 0;


$beggars[$ind]+= shift @values and $ind >= $#beggars ? $ind = 0 : $ind++ while @values;

Or, using Tye::Cycle from CPAN a very clean solution:

use Tie::Cycle;

my @values = (1..5);
my @beggars;
$#beggars = $ARGV[0]//1;

tie my $ind, 'Tie::Cycle', [ 0..$#beggars ];

$beggars[$ind]+= $_ for @values;
Collapse
choroba profile image
E. Choroba

Going functional in Perl:

#!/usr/bin/perl
use warnings;
use strict;

use List::Util      qw{ sum };
use List::MoreUtils qw{ part };

sub beggars {
    my ($money, $number_of_beggars) = @_;
    my $i = 0;
    [ map sum(@$_), part { $i++ % $number_of_beggars } @$money ];
}

use Test::More tests => 2;
is_deeply beggars([1, 2, 3, 4, 5], 2), [9,6];
is_deeply beggars([1, 2, 3, 4, 5], 3), [5, 7, 3];
Collapse
delixx profile image
DeLiXx

An computation-on-demand approach in js

function beggars(arr, count) {
    return (i) => {
        let loot = 0;
        for(; i < arr.length; i += count) loot += arr[i];
        return loot;
    }
}
let a = beggars([1,2,3,4,5],2);
Collapse
aminnairi profile image
Amin

Elm

module EnglishBeggars exposing (englishBeggars)


currentBeggar distribution turn donation =
    donation -- (0, 1)
        |> Tuple.first -- 0
        |> modBy distribution -- modBy 0 = 0, modBy 1 = 1, modBy 2 = 0
        |> (==) turn -- 0 == 0 = True, 1 == 0 = False, 0 == 0 = True


distribute donations distribution turn =
    donations -- [1, 2, 3, 4, 5]
        |> List.indexedMap Tuple.pair -- [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5)]
        |> List.filter (currentBeggar distribution turn) -- [(0, 1), (2, 3), (4, 5)]
        |> List.map Tuple.second -- [1, 3, 5]
        |> List.sum -- 9


englishBeggars : List Int -> Int -> List Int
englishBeggars donations distribution =
    List.range 0 (distribution - 1) -- [0, 1]
        |> List.map (distribute donations distribution) -- [9, 6]

Tests

module EnglishBeggarsTest exposing (suite)

import EnglishBeggars exposing (englishBeggars)
import Expect exposing (equal)
import Test exposing (Test, describe, test)


suite : Test
suite =
    describe "English beggars"
        [ test "Should return [9, 6] for a donation of [1, 2, 3, 4, 5] between 2 beggars" <|
            \_ -> englishBeggars [ 1, 2, 3, 4, 5 ] 2 |> equal [ 9, 6 ]
        , test "Should return [5, 7, 3] for a donation of [1, 2, 3, 4, 5] between 3 beggars" <|
            \_ -> englishBeggars [ 1, 2, 3, 4, 5 ] 3 |> equal [ 5, 7, 3 ]
        , test "Should return [1, 2, 3, 0] for a donation of [1, 2, 3] between 4 beggars" <|
            \_ -> englishBeggars [ 1, 2, 3 ] 4 |> equal [ 1, 2, 3, 0 ]
        ]
Collapse
pmkroeker profile image
Peter

In Go!

amts below could also be a []float64 if required. All posted examples were with ints so that is what I used.

func beggars(amts []int, beggarCount int) []int {
    // defaults to slice of 0's
    sums := make([]int, beggarCount)
    for idx, amt := range amts {
        beggarId := idx % beggarCount
        sums[beggarId] += amt
    }
    return sums
}

Go Playground Example