-4

I have an function that takes an array as an input. How can I modify it to work with variable arguments as well as arrays. For example I want arrSum(1,2,3) == arrSum([1,2,3]) to return True i.e. both should return 6

I have already asked this same question for JavaScript but when I tried to implement the same technique in Python I am getting totally unexpected errors.

This is what I have tried to do based on my current knowledge of python.

def arrSum(*args):
    listOfArgs = []
    listOfArgs.extend([*list(args)])
    return sum(listOfArgs)

It works perfectly for arrSum(1,2,3) but returns error for arrSum([1,2,3]) arrSum(1,2,3) returns 6 which is the expected output. But `arrSum([1,2,3]) returns the following error:

Traceback (most recent call last):
  File "python", line 7, in <module>
  File "python", line 4, in arrSum
TypeError: unsupported operand type(s) for +: 'int' and 'list'

Doing a print(listOfArgs) before return sum(listOfArgs) prints [[1,2,3]].

REPL.it link of the function

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Rohit Kumar
  • 19
  • 2
  • 7
  • 1
    "getting totally unexpected results" -- please include input, expected output and actual output in your question – khelwood Jan 08 '18 at 13:22
  • @khelwood I added a link to repl for that but I shall update the question too :) – Rohit Kumar Jan 08 '18 at 13:23
  • What would the expected output of `arrSum(1)` be? `1` or an error because the single argument isn't iterable? – Tom Dalton Jan 08 '18 at 13:24
  • 1
    Have you tried to add a `print(listOfArgs)` in your function yet? Just before the `sum()`? – Martijn Pieters Jan 08 '18 at 13:24
  • @MartijnPieters Yes I tried that and it returns `[[1, 2, 3]]` – Rohit Kumar Jan 08 '18 at 13:26
  • @RohitKumar: so you have a bug in your code there, you made an incorrect assumption somewhere. Note that Python is not Javascript, you can't apply the same principles. Note that the JS code uses `.concat(...arr)`, not `.concat(arr)`, for example! – Martijn Pieters Jan 08 '18 at 13:32
  • You need to check if your argument is a list of not, and act accordingly. A simple loop with `isinstance` will do. – IMCoins Jan 08 '18 at 13:33
  • @MartijnPieters I know it it `.concat(...arr)` and that is the reason I did `listOfArgs.extend([*list(args)])` – Rohit Kumar Jan 08 '18 at 13:35

3 Answers3

2

The Javascript spread operator treats an array of values and a array containing another array of values exactly the same:

[].concat(...[1, 2])
Array [ 1, 2 ]
[].concat(...[[1, 2]])
Array [ 1, 2 ]

It essentially flattens multiple arrays:

[].concat(...[[1, 2], [3, 4]])
Array [ 1, 2, 3, 4 ]

but only by one layer:

[].concat(...[[[1, 2]]])
[[ 1, 2 ]]

Python is much more exact. There is no implicit flattening, you have to do it explicitly:

def arrSum(*args):
    if len(args) == 1 and isinstance(args[0], list):
        args = args[0]
    return sum(args)

If you wanted to replicate the support for multiple lists (so arrSum([1, 2], [3, 4]), I'd just reuse the Flatten (an irregular) list of lists top answer:

import collections

def flatten(l):
    for el in l:
        if isinstance(el, collections.Iterable) and not isinstance(el, (str, bytes)):
            yield from flatten(el)
        else:
            yield el

def arrSum(*args):
    return sum(flatten(args))
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
0

You need to check if you have a list, or an int. :)

Here, this function assumes you will only receive integers and list of integers.

def arrSum(*args):
    ret = 0
    total = 0
    for elem in args:
        #   If you have a list, adds the sum of the list, else, simply add the int.
        if isinstance(elem, list):
            total += len(elem)
            ret += sum(elem)
        else
            total += 1
            ret += elem

    average = ret / total
    return ret, average

print arrSum([1, 2, 3])
print arrSum(1, 2, 3)
# both outputs : 6
IMCoins
  • 3,149
  • 1
  • 10
  • 25
  • This works for only doing sum. But how will I implement a average with it. I mean what if I do `average(1,2,3,[1,2,3])`. I think I might have to write a helper function for that – Rohit Kumar Jan 08 '18 at 13:40
  • Well, you ask for a function that would sum all the numbers, not make the average. But this would also be simple to do. :) – IMCoins Jan 08 '18 at 13:42
  • I don't think it will simple to calculate the number of arguments – Rohit Kumar Jan 08 '18 at 13:43
  • See my updated answer to see how easy it was to get the average. – IMCoins Jan 08 '18 at 13:46
  • It completly fails for `arrSum([1,2,3],4,5,6)`. But martijin's answer solves that problem. Thanks – Rohit Kumar Jan 08 '18 at 13:49
  • It does not for me, since it returns an `int`, and prints `3` for the average of (1+2+3+4+5+6) = 21, divided by 6, it makes 3. If you make the division using **float**, you will correctly have 3.5. – IMCoins Jan 08 '18 at 13:56
0

After taking inspiration from Martijin's answer I came up with a solution of my own(and even came up with a spread function for python):-

def spread(arg):

 ret = []

 for i in arg:

   if isinstance(i,list):

     ret.extend(i)

   else:

     ret.append(i)

 return ret

def arrSum(*args):
    a = list(args)
    m = []
    m.extend(spread(a))
    return sum(m)
print(arrSum([1, 2, 3]))
print(arrSum(1, 2, 3))
print(arrSum([1, 2, 3], [4], 5)) 

Working REPL.it link

Rohit Kumar
  • 19
  • 2
  • 7
  • There is no point in extending an empty list. You don't need to convert `args` to a list either, as all you do is iterate over the sequence. So you can just use `return sum(spread(args))`. – Martijn Pieters Jan 08 '18 at 15:17
  • Your `spread()` function is otherwise a more limited version of the `flatten()` function I referenced in my answer (limited to a single level of values and lists). – Martijn Pieters Jan 08 '18 at 15:18