How to count len of strings in a list without built-in function?

Question:

How can I create a function count_word in order to get the result like this:

x = ['Hello', 'Bye']
print(count_word(x))
# Result must be [5, 3] 

without using len(x[index]) or any built-in function?

Asked By: Mustafa Kareem

||

Answers:

x = ['Hello', 'Bye']
results = []
for single_string in x:
    string_length = 0
    for i in single_string: string_length += 1
    results.append(string_length)
print(results)
Answered By: Parakiwi
x = ['Hello', 'Bye']
def count(x):
    i=0
    for j in x:i+=1
    return i
print(list(map(count, x)))
Answered By: jivan patel

Since you’re not allowed to use built-in functions, you have to iterate over each string in the list and over all characters of each word as well. Also you have to memorize the current length of each word and reset the counter if the next word is taken. This is done by re-assigning the counter value to 0 (length = 0) before the next inner iteration will be started:

def count_word(x):
    result = []
    for word in x:
        length = 0
        for char in word:
            length += 1
        result.append(length)
    return result

Please note that this is probably the no-brainer par excellence. However, Python offers some interesting other approaches to solve this problem. Here are some other interesting examples, which of course need to be adapted.


While this should answer your questions, I would like to add some notes about performance and why it is better to use built-in functions:

Generally spoken, built-in functions are doing the iteration under the hood for you or are even faster by e.g. simply getting the array’s length from the CPython list head structure (emphasis mine):

How are lists implemented in CPython?

CPython’s lists are really variable-length arrays, not Lisp-style linked lists. The implementation uses a contiguous array of references to other objects, and keeps a pointer to this array and the array’s length in a list head structure.

This makes indexing a list a[i] an operation whose cost is independent of the size of the list or the value of the index.

When items are appended or inserted, the array of references is resized. Some cleverness is applied to improve the performance of appending items repeatedly; when the array must be grown, some extra space is allocated so the next few times don’t require an actual resize.

(Credits also to Ken Y-N, see How does len(array) work under the hood)

Generally, it is better to use built-in functions whenever you can, because you seldom can beat the performance of the underlying implementation (e.g. for C-based Python installations):

def count_word_2(x):
    return [len(word) for word in x]

You can see that if you time the two given functions:

In [1]: from timeit import timeit

In [2]: statement = 'count_word(["Hello", "Bye"])'

In [3]: count_word_1 = """
   ...: def count_word(x):
   ...:     result = []
   ...:     for word in x:
   ...:         length = 0
   ...:         for char in word:
   ...:             length += 1
   ...:         result.append(length)
   ...:     return result
   ...: """

In [4]: count_word_2 = """
   ...: def count_word(x):
   ...:     return [len(word) for word in x]
   ...: """

In [5]: timeit(stmt=statement, setup=count_word_1, number=10000000)
Out[5]: 4.744415309000033

In [6]: timeit(stmt=statement, setup=count_word_2, number=10000000)
Out[6]: 2.7576589090022026

If also a little bit of cheating is allowed (using string dunder method __len()__ instead of built-in function len()), you can get some performance back (credits to HeapOverflow):

In [7]: count_word_3 = """
...: def count_word(x):
...:     return [word.__len__() for word in x]
...: """

In [8]: timeit(stmt=statement, setup=count_word_3, number=10000000)
Out[8]: 3.313732603997778

So a good rule of thumb is: Do whatever you can with built-in functions. They are more readable and faster.

Answered By: colidyre

Another possibility using inline assignment (Python >= 3.8)

def count(word):
    c = 0
    return [[_ for _ in word if (c := c+1)], c][1]

words = ['Hello', 'How are you', 'Bye!']

>>> [count(w) for w in words]
[5, 11, 4]

Or even passing c as an input argument (defaulted to 0) could be used as an accumulated result from previous counts:

def count(word, c=0):
    return [[_ for _ in word if (c := c+1)], c][1]

ctot = [0]
[ctot.append(count(w, ctot[-1])) for w in words]  # sort of equivalent to built-in 'reduce'

>>> ctot  # or ctot[1:] to ignore the first zero
[0, 5, 16, 20]
Answered By: Martí
Categories: questions Tags:
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.