Find the total amount of characters in a list - python

I'm kind of stuck on an Edabit Challenge (Link: https://edabit.com/challenge/S9KCN5kqoDbhNdKh5 ),
and the objective is to basically find the total amount of characters in a list (all of the values are strings) and return that value.
Some examples are provided:
count_characters([
"###",
"###",
"###"
]) ➞ 9
count_characters([
"22222222",
"22222222",
]) ➞ 16
count_characters([
"------------------"
]) ➞ 18
count_characters([]) ➞ 0
count_characters(["", ""]) ➞ 0
My current code to achieve the goal is:
def count_characters(lst):
print(lst) #I print the list
count = 0 #Setting my count var to 0
for item in lst: #Using a for loop to iterate through the list's values
count += len(lst) #Getting the length of each value and adding it to the variable 'count'
print(len(lst)) #I also print the length of each value
print(count) #Outside the for loop, I print the count variable
return count #Then I return it
(There are multiple tests to check if the entire function works btw)
When I ran the code, this is what the console outputs:
['###', '###', '###']
3
3
3
9
Test Passed
['22222222', '22222222']
2
2
4
FAILED: 4 should equal 16
ERROR: Traceback:
in <module>
File "./frameworks/python/cw-2.py", line 28, in assert_equals
expect(actual == expected, message, allow_raise)
File "./frameworks/python/cw-2.py", line 18, in expect
raise AssertException(message)
cw-2.AssertException: 4 should equal 16
I don't understand why the code did not find the length of the 2nd test, but worked for the 1st test.
Thank you

A very minor mistake...in the loop
for item in lst:
count+=len(item)
You are doing
for item in lst:
count+=len(lst)
As in the 1st case, both length of list & length of each element is 3, the code gave the desired output but not for the 2nd case

Related

Python Loop Equivalent Excel Offset equivalent?

I am new to programming in Python and am trying to write a For/While loop that returns a value relative to another in a list based on the current value in the loop. The closest analog from excel I can think of is the OFFSET function, but my searches have failed to come up with an answer. Also, I believe VBA in excel allows you to use relative cells instead of absolute. Maybe this is a better analog.
As an example:
Suppose I have a simple list abc = [2,5,21,54,23,12,23] and I want to create a variable that is the current value in the loop divided by the prior value. So in this instance 5 would be divided by 2, 21 divided by 5, etc.
An example of the code would like something like:
for val in abc:
xyz = val/val[-1]
print(xyz)
I understand I would need to write an exception for the first iteration and the syntax is incorrect, but just trying to convey my message.
My search found that pandas has an ability to do this, but I was wanting to know if it is possible to do without pandas. Would appreciate any input.
You can't just loop by value since the value has no context of its position in the list. One way is to loop by index:
# start from 1, not 0
for i in range(1, len(abc)):
xyz = abc[i] / abc[i - 1]
print(xyz)
Or, zip the list with its tail:
for prev, cur in zip(abc, abc[1:]):
xyz = cur / prev
print(xyz)
For intuition on why this works:
prev
v
abc -> 1 2 3 4 5 6 7
abc[1:] -> 2 3 4 5 6 7
^
cur
You can use enumerate to do this:
abc = [2,5,21,54,23,12,23]
for index, val in enumerate(abc):
print("VALUE: {}, PREVIOUS: {}, RESULT: {}".format(val, abc[index-1], val/abc[index-1]))
However, there is a gotcha here. Python can have negative indexes. Where it wraps around the the other side of the list. If you do the above, the first time through the loop you will see it do this:
VALUE: 2, PREVIOUS: 23, RESULT: 0.08695652173913043
So, to protect against that, you could do this:
abc = [2,5,21,54,23,12,23]
for index, val in enumerate(abc):
if index > 0:
print("VALUE: {}, PREVIOUS: {}, RESULT: {}".format(val, abc[index-1], val/abc[index-1]))
else:
print("First Value")
Resulting in this output:
First Value
VALUE: 5, PREVIOUS: 2, RESULT: 2.5
VALUE: 21, PREVIOUS: 5, RESULT: 4.2
VALUE: 54, PREVIOUS: 21, RESULT: 2.5714285714285716
VALUE: 23, PREVIOUS: 54, RESULT: 0.42592592592592593
VALUE: 12, PREVIOUS: 23, RESULT: 0.5217391304347826
VALUE: 23, PREVIOUS: 12, RESULT: 1.9166666666666667
Pythonic style:
abc = [2,5,21,54,23,12,23]
gen = (abc[i + 1] / v for i, v in enumerate(abc) if i + 1 < len(abc))
for i in gen:
print(i)
Result:
2.5
4.2
2.5714285714285716
0.42592592592592593
0.5217391304347826
1.9166666666666667

python for and while loop for tuples

I'm a beginner in python and was wondering why this function doesn't work. It is syntactically correct.
This function is supposed to collect every odd tuple item and I used a for loop as follows:
def oddTuples(aTup):
result = ()
for i in aTup:
if i % 2 == 0:
result += (aTup[i],)
return result
This is the 'correct' answer using while loop.
def oddTuples(aTup):
# a placeholder to gather our response
rTup = ()
index = 0
# Idea: Iterate over the elements in aTup, counting by 2
# (every other element) and adding that element to
# the result
while index < len(aTup):
rTup += (aTup[index],)
index += 2
return rTup
If anybody can help me, it would be much appreciated!
UPDATE
Okay, I got the problem, by 'i' I was merely collecting the real value within that tuple. I've fixed that, but this code is catching only some of the odd-idexed items, not all of them....
def oddTuples(aTup):
result = ()
for i in aTup:
index = aTup.index(i)
if index % 2 == 0:
result += (aTup[index],)
return result
Your for loop is iterating over the values in aTup, not the index of the values.
It appears your want your code to iterate over the index of the values or through a range of numbers starting with 0 and ending with the length of the tuple minus one and then use that number as the index to pull the value out of the tuple.
I didn't catch it on one go since it was syntactically correct too, but the error you are having is due to you iterating over the objects of the tuple (aTup) and not the indices. See here:
for i in aTup: # <-- For each *object* in the tuple and NOT indices
if i % 2 == 0:
result += (aTup[i],)
To fix the problem, use range() and len() over the aTup so that it iterates over the indices of the tuple instead, and change the if statement accordingly:
for i in range(len(aTup)):
if aTup[i] % 2 == 0:
result += (aTup[i],)
An alternative solution is to keep your object iterations but append the object directly to the result tuple instead of indexing:
for i in aTup:
if i % 2 == 0:
result += (i,)
Hope ths helped!
The reason is you are not using index..In below code i is not an index but the element in tuple but you are calling aTup[i] assuming i is an index which is not.
The below code will work fine - No need of doing aTup[i] or range.
def oddTuples(aTup):
result = ()
for i in aTup:
if i % 2 == 0:
result += (i,)
return result
Try replacing
def oddTuples(aTup):
result = ()
for i in aTup:
index = aTup.index(i)
if index % 2 == 0:
result += (aTup[index],)
return result
With
def oddTuples(aTup):
result = ()
for i in aTup:
index = aTup.index(i)
result += (aTup[index],)
return result
To fix you issue of it only doing the even numbered ones.
In simple words , if your tuple is
tup = (1, 2, 3, 4, 5 , 1000);
When your code is checking if each item is % 2 == 0 or not which is not what you want, from your description , you want only the items with odd index
So if you try the tuple above , you will get the following error:
IndexError: tuple index out of range , because for the 1000 it satisfy your condition and will do what is said in the if , trying to add the aTup(1000) (element of index 1000 in your input tuple) which doesn't exist as the tuple is only of 6 elements to your resultTuple
For this for loop to work , you can use the following method
def oddTuples(aTup):
result = ()
for i in aTup:
index = tup.index(i) # getting the index of each element
if index % 2 == 0:
result += (aTup[index],)
print(aTup[index])
return result
# Testing the function with a tuple
if __name__ == "__main__":
tup = (1, 2, 3, 7, 5, 1000, 1022)
tup_res = oddTuples(tup)
print(tup_res)
The result of this will be
1
3
5
1022
(1, 3, 5, 1022)
Process finished with exit code 0

Incorrect line number returned on specifying Start parameter in enumerate Python

I have slight confusion regarding the start parameter in enumerate function,as i recently started working on python i don't have much idea how it is supposed to work.
Suppose i have an example file below:
Test 1
Test 2
Test 3
This is the first line [SB WOM]|[INTERNAL REQUEST]|[START] which is the start of message
Name : Vaibhav
Designation : Technical Lead
ID : 123456
Company : Nokia
This is the sixth line [SB WOM]|[INTERNAL REQUEST]|[END] which is the end of message
Now when i run the below code :
path =("C:/Users/vgupt021/Desktop")
in_file = os.path.join(path,"KSClogs_Test.txt")
fd = open(in_file,'r')
for linenum,line in enumerate(fd) :
if "[SB WOM]|[INTERNAL REQUEST]|[START]" in line:
x1 = linenum
print x1
break
for linenum,line in enumerate(fd,x1):
if "[SB WOM]|[INTERNAL REQUEST]|[END]" in line:
print linenum
break
I get the linenum returned as 3 and 7, I am not clear why it is not returned as 3,8.Since the index number of line "[SB WOM]|[INTERNAL REQUEST]|[END]" is 8 and not 7, how the start parameter makes the difference in second part of the loop.
Since the file iterator object has read the first four lines, when running the second for loop, it starts from where it stopped. The previous iteration stopped at line 3 (assuming we start counting from 0), the next for loop starts at line 4.
Therefore, the enumerate of the second loop should start from x1 + 1 not x1 as the line with index x1 was already covered in the previous loop; last line of first loop:
for linenum, line in enumerate(fd, x1+1):
...
Try this code
x = range(10)
for i, e in enumerate(x):
if i == 4:
print i
st = i
break
for i, e in enumerate(x, st):
print i
And you will see this output:
4
4 5 6 7 8 9 10 11 12 13
So, what does the second parameter of enumerate? Well, it's the starting value of the index of enumerate. The iterable variable x is enumerated again from the beginning but the values of i at different iteration is shifted by the value of st.
Instead of having the values of i as 0, 1, 2, etc., we have 4, 5, 6, etc.
I think that explains why you have the incorrect line number in your code.

Incorrect output dictionary from user's input

I need output to be in the form
{0: {1:11,2:13}, 1: {0:11,3:14}}
But it comes out to
{0: {1:['11'],2:['13']}, 1: {0:['11'],3:['14']}}
using this
graph = {}
N,w = map(int,raw_input().split())
# print N, w
for x in range(0,C):
i,j,c = raw_input().split()
graph.setdefault(int(i), {}).setdefault(int(j),[]).append(w)
print graph
on INPUT
1st line: Ignore N=4, while C=4 is the number of lines.
2nd line: i,j are vertices, w is the edge weight.
4 4
0 1 11
0 2 13
1 0 11
1 3 14
You are setting lists as values inside your nested dictionary in the following line -
graph.setdefault(int(i), {}).setdefault(int(j),[]).append(w)
This is why you are getting values inside list, if you are 100% sure that the key:value pairs inside the nested dictionary would always be unique, then you can simply set the value to the key. Example -
graph.setdefault(int(i), {})[int(j)] = w

Comparing two CSV files in Python when rows have multiple values

I have two CSV files that I want to compare one looks like this:
"a" 1 6 3 1 8
"b" 15 6 12 5 6
"c" 7 4 1 4 8
"d" 14 8 12 11 4
"e" 1 8 7 13 12
"f" 2 5 4 13 9
"g" 8 6 9 3 3
"h" 5 12 8 2 3
"i" 5 9 2 11 11
"j" 1 9 2 4 9
So "a" possesses the numbers 1,6,3,1,8 etc. The actual CSV file is 1,000s of lines long so you know for efficiency sake when writing the code.
The second CSV file looks like this:
4
15
7
9
2
I have written some code to import these CSV files into lists in python.
with open('winningnumbers.csv', 'rb') as wn:
reader = csv.reader(wn)
winningnumbers = list(reader)
wn1 = winningnumbers[0]
wn2 = winningnumbers[1]
wn3 = winningnumbers[2]
wn4 = winningnumbers[3]
wn5 = winningnumbers[4]
print(winningnumbers)
with open('Entries#x.csv', 'rb') as en:
readere = csv.reader(en)
enl = list(readere)
How would I now search cross reference number 4 so wn1 of CSV file 2 with the first csv file. So that it returns that "b" has wn1 in it. I imported them as a list to see if I could figure out how to do it but just ended up running in circles. I also tried using dict() but had no success.
If I understood you correctly, you want to find the first index (or all indexes) of numbers in entries that are winning. If you want it, you can do that:
with open('winningnumbers.csv', 'rb') as wn:
reader = csv.reader(wn)
winningnumbers = list(reader)
with open('Entries#x.csv', 'rb') as en:
readere = csv.reader(en)
winning_number_index = -1 # Default value which we will print if nothing is found
current_index = 0 # Initial index
for line in readere: # Iterate over entries file
all_numbers_match = True # Default value that will be set to False if any of the elements doesn't match with winningnumbers
for i in range(len(line)):
if line[i] != winningnumbers[i]: # If values of current line and winningnumbers with matching indexes are not equal
all_numbers_match = False # Our default value is set to False
break # Exit "for" without finishing
if all_numbers_match == True: # If our default value is still True (which indicates that all numbers match)
winning_number_index = current_index # Current index is written to winning_number_index
break # Exit "for" without finishing
else: # Not all numbers match
current_index += 1
print(winning_number_index)
This will print the index of the first winning number in entries (if you want all the indexes, write about it in the comments).
Note: this is not the optimal code to solve your problem. It's just easier to undestand and debug if you're not familiar with Python's more advanced features.
You should probably consider not abbreviating your variables. entries_reader takes just a second more to write and 5 seconds less to understand then readere.
This is the variant that is faster, shorter and more memory efficient, but may be harder to understand:
with open('winningnumbers.csv', 'rb') as wn:
reader = csv.reader(wn)
winningnumbers = list(reader)
with open('Entries#x.csv', 'rb') as en:
readere = csv.reader(en)
for line_index, line in enumerate(readere):
if all((line[i] == winningnumbers[i] for i in xrange(len(line)))):
winning_number_index = line_index
break
else:
winning_number_index = -1
print(winning_number_index)
The features that might me unclear are probably enumerate(), any() and using else in for and not in if. Let's go through all of them one by one.
To understand this usage of enumerate, you'll need to understand that syntax:
a, b = [1, 2]
Variables a and b will be assigned according values from the list. In this case a will be 1 and b will be 2. Using this syntax we can do that:
for a, b in [[1, 2], [2, 3], ['spam', 'eggs']]:
# do something with a and b
in each iteration, a and b will be 1 and 2, 2 and 3, 'spam' and 'eggs' accordingly.
Let's assume we have a list a = ['spam', 'eggs', 'potatoes']. enumerate() just returns a "list" like that: [(1, 'spam'), (2, 'eggs'), (3, 'potatoes')]. So, when we use it like that,
for line_index, line in enumerate(readere):
# Do something with line_index and line
line_index will be 1, 2, 3, e.t.c.
any() function accepts a sequence (list, tuple, e.t.c.) and returns True if all the elements in it are equal to True.
Generator expression mylist = [line[i] == winningnumbers[i] for i in range(len(line))] returns a list and is similar to the following:
mylist = []
for i in range(len(line)):
mylist.append(line[i] == winningnumbers[i]) # a == b will return True if a is equal to b
So any will return True only in cases when all the numbers from entry match the winning numbers.
Code in else section of for is called only when for was not interrupted by break, so in our situation it's good for setting a default index to return.
Having duplicate numbers seems illogical but if you want to get the count of matched numbers for each row regardless of index then makes nums a set and sum the times a number from each row is in the set:
from itertools import islice, imap
import csv
with open("in.txt") as f,open("numbers.txt") as nums:
# make a set of all winning nums
nums = set(imap(str.rstrip, nums))
r = csv.reader(f)
# iterate over each row and sum how many matches we get
for row in r:
print("{} matched {}".format(row[0], sum(n in nums
for n in islice(row, 1, None))))
Which using your input will output:
a matched 0
b matched 1
c matched 2
d matched 1
e matched 0
f matched 2
g matched 0
h matched 1
i matched 1
j matched 2
presuming your file is comma separated and you have a number per line in your numbers file.
If you actually want to know which numbers if any are present then you need to iterate over the number and print each one that is in our set:
from itertools import islice, imap
import csv
with open("in.txt") as f, open("numbers.txt") as nums:
nums = set(imap(str.rstrip, nums))
r = csv.reader(f)
for row in r:
for n in islice(row, 1, None):
if n in nums:
print("{} is in row {}".format(n, row[0]))
print("")
But again, I am not sure having duplicate numbers makes sense.
To group the rows based on how many matches, you can use a dict using the sum as the key and appending the first column value:
from itertools import islice, imap
import csv
from collections import defaultdict
with open("in.txt") as f,open("numbers.txt") as nums:
# make a set of all winning nums
nums = set(imap(str.rstrip, nums))
r = csv.reader(f)
results = defaultdict(list)
# iterate over each row and sum how many matches we get
for row in r:
results[sum(n in nums for n in islice(row, 1, None))].append(row[0])
results:
defaultdict(<type 'list'>,
{0: ['a', 'e', 'g'], 1: ['b', 'd', 'h', 'i'],
2: ['c', 'f', 'j']})
The keys are numbers match, the values are the rows ids that matched the n numbers.

Categories

Resources