8
\$\begingroup\$

There have been some tax rate calculator problems here that depend on defined rates (specifically US federal rates), but none that have done it for arbitrary lists.

Your task is to build a program or function that will take an arbitrary list of marginal tax brackets and calculate the effective amount of tax paid on a given amount of taxable income.

Your program will take as input:

  • N pairs of numbers in the form (amt, rate), each representing a tax bracket.

    • amt is the amount of taxable income above which at which the tax rate starts to apply. These amounts will all be integers, and appear in increasing order.

    • rate is the tax rate, expressed as a decimal percentage from 0 to 100 inclusive.

  • The amount of taxable income, as a nonnegative integer.

If no rate is specified for $0, the rate is assumed to be 0%.

Alternatively, your program can also take as input two lists (amt1, amt2, ...) and (rate1, rate2, ...), containing the amounts and rates in the same order.

Your program will then output the payable tax, either to two decimal places, or rounded down to the nearest whole currency unit.


An example input (the most recent Canadian tax brackets, from 2016, with the basic personal tax credit applied and nothing else):

11474 15
45282 20.5
90563 26
140388 29
200000 33

393216

And its corresponding output:

108357.07  or  108357

The shortest program in any language to do this wins.

\$\endgroup\$
  • \$\begingroup\$ Is input as [[amt,amt,amt],[rate,rate,rate]] OK? Or does it have to be pairs of [amt,rate] ? \$\endgroup\$ – Emigna Jun 27 '16 at 14:54
  • \$\begingroup\$ Could input be 1 list of amounts, 1 list of rates and the tax income value (3 inputs in total)? \$\endgroup\$ – Emigna Jun 27 '16 at 15:02
  • \$\begingroup\$ Well, I've specified (amt, rate), but it doesn't fundamentally change the problem to accept the other list, so I'll modify it so you can. \$\endgroup\$ – Joe Z. Jun 27 '16 at 15:38
2
\$\begingroup\$

05AB1E, 35 24 bytes

vy³ï‚{0è)˜}³ï‚˜¥²ø€PTn/O

Explanation

vy                         # for each amount
  ³ï‚                      # pair with taxable income
     {0è                   # get min
        )˜}                # add to list
           ³ï‚˜            # add income to the end of the list
               ¥           # get deltas
                ²ø         # zip with tax rates
                  €P       # map product on each pair of [amount in tax bracket,rate]
                    Tn/    # divide by 100
                       O   # sum
                           # implicitly display result

Try it online

\$\endgroup\$
  • \$\begingroup\$ Do human beings actually read and understand 05AB1E code? \$\endgroup\$ – DavidC Jun 27 '16 at 14:47
  • \$\begingroup\$ @DavidC: I've learned to understand most of it. I'll write up an explanation though. Would have done it already, but wasn't sure if both input methods were OK. \$\endgroup\$ – Emigna Jun 27 '16 at 14:52
3
\$\begingroup\$

Haskell, 67 66 bytes

Thanks Damien for -1 byte.

This solution is in the form of an infix function, ?, of type (Integral b, RealFrac r) => [(r, r)] -> r -> b. The helper function, #, does the required calculations while ? serves to handle the IO specifications.

a?b=floor$reverse a#b
((m,p):r)#i|v<-min i m=p/100*(i-v)+r#v
_#_=0
\$\endgroup\$
  • \$\begingroup\$ ((m,p):r)#i|v<-min i m=p/100*(i-v)+r#v saves 1 byte \$\endgroup\$ – Damien Jun 27 '16 at 11:22
  • \$\begingroup\$ Thanks @Damien! I didn't know you could assign values inside a guard like that. \$\endgroup\$ – ankh-morpork Jun 27 '16 at 13:48
2
\$\begingroup\$

Mathematica 85 82 bytes

Derived from Josh O'Brien's code in R.

d_~f~i_:=Tr@Thread[Differences@((i~Min~#&/@d[[All,1]]~Append~∞))d[[All,2]]/100.]

Usage

f[{{11474, 15}, {45282, 20.5}, {90563, 26}, {140388, 29}, {200000, 33}}, 393216]

108357.

\$\endgroup\$
  • \$\begingroup\$ So this has been asked before, just not on this particular site. Makes sense. \$\endgroup\$ – Joe Z. Jun 26 '16 at 0:40
1
\$\begingroup\$

Matlab, 79 bytes

Assuming we can take amt and rate as separate column vectors:

function[]=f(a,b,c)
t=sort([0;a;c]);fix(sum(diff(t).*[0;b/100].*(t(2:end)<=c)))

If we can't (then a is a two-column matrix of amt and rate) it's 87 bytes.

function[]=f(a,c)
t=sort([0;a(:,1);c]);fix(sum(diff(t).*[0;a(:,2)/100].*(t(2:end)<=c)))

Explanation:

f(a,b,c)           -- takes amt, rate and income
[0;a;c]            -- gives a vector of amounts with 0 and the income amount
t=sort(...)        -- sort this vector
diff(t)            -- gives us amounts to be taxed in every bracket
diff(t).*[0;b/100] -- tax in every bracket
.*(t(2:end)<=c)    -- take only entries lower than the income
sum(...)           -- sum everything
fix(...)           -- round towards 0
\$\endgroup\$
1
\$\begingroup\$

JavaScript (ES6), 60 bytes

(a,n)=>a.map(([b,r])=>{t+=n>b&&(n-b)*(r-p);p=r},p=t=0)|t/100

a is an array of arrays of band and rate, which would be [[11474, 15], [45282, 20.5], [90563, 26], [140388, 29], [200000, 33]] for the given example, and n is the income (393216).

\$\endgroup\$
  • \$\begingroup\$ Can you provide more information on the input format? \$\endgroup\$ – Joe Z. Jun 27 '16 at 4:23
0
\$\begingroup\$

Swift, 61 bytes

zip(zip(m+[i],[0]+m).map{$0-$1},[0]+r).map{$0*$1}.reduce(0,+)

Ungolfed, with test cases:

struct TaxContext {
    let marginLowerBound: [Double]
    let rates: [Double]
}

// ungolfed
extension TaxContext {
    func calcAfterTaxIncome(_ income: Double) -> Double {
        //drop first element (0), append income
        let upper = marginLowerBound + [income]
        let lower = [0] + marginLowerBound

        let incomeInEachMargin = zip(upper, lower)
            .map{ upperBound, lowerBound in upperBound - lowerBound }

        let taxInEachMargin = zip(incomeInEachMargin, [0]+rates)
            .map{ incomeInMargin, rate in incomeInMargin * rate }

        let totalTax = taxInEachMargin.reduce(0, +)

        print(upper)
        print(lower)
        print(incomeInEachMargin)
        print(rates)
        print(taxInEachMargin)

        return totalTax    
    }
}

// golfed
extension TaxContext {
    // computed properties to serve as aliases for the golfed version
    var m: [Double] { return self.marginLowerBound }
    var r: [Double] { return self.rates }

    func f(_ i: Double) -> Double {
        return zip(zip(m+[i],[0]+m).map{$0-$1},[0]+r).map{$0*$1}.reduce(0,+)
    }
}

let testCase1 = TaxContext(
    marginLowerBound: [10_000, 20_000, 30_000],
    rates: [0.1, 0.2, 0.3, 0.4]
)
let result1 = testCase1.calcAfterTaxIncome(70_000)
print(result1)


let testCase2 = TaxContext(
    marginLowerBound: [11474 , 45282, 90563, 140388, 200000],
    rates: [0.15, 0.205, 0.26, 0.29, 0.33]
)
let result2 = testCase2.calcAfterTaxIncome(393216)
print(result2)
\$\endgroup\$

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.