Pythonic way to print data this data with headers - python

I have this dataset:
[('Stuff', ' ')
('Available in several colors.', ' ')
('A fits B by Sometimes.', ' ')
('handle $148', 'A ')
('handle base $23', 'A ')
('mirror base $24', 'A ')
(' handle $31', 'B ')
('handle base, $23', 'B ')
('Mirror $24', 'B ')
]
What I need to do is print this data with a "header" based off the second item in the list. Only printing the header when it changes. In this sample, the only 3 options are " ", "A ", and "B ". But in my actual data set there could be 100+ different options, so hard coding them in is not really an options.
I want the printed data to look like this (skipping the " ")
Stuff.
Available in several colors.
A fits B by Sometimes.
A ----------------------
handle $148
handle base $23
mirror base $24
B ----------------------
handle $31
handle base, $23
Mirror $24
the only way to do this I can think of is to hard code the values in, but with 100+ possibilities this would take forever. There must be a better way.
a_printed = False
b_printed = False
for item in list1:
if item[1] == ' ':
print(item[0])
elif item[1] == 'A ':
if a_printed != True:
print('A --------------')
a_printed = True
print(item[0])
elif item[1] == 'B ':
if b_printed != True:
print('B --------------')
b_printed = True
print(item[0])
Any help is appreciated.

You don't need to hard-code the header values, just set a variable to the header and test if the new header is different.
last_header = ' '
for value, header in list1:
if header != last_header:
print(header, '--------------')
last_header = header
print value

Related

Merge 2 elements together if the elements contains

I have a very messy data I am noticing patterns where ever there is '\n' end of the element, it needs to be merged with single element before that.
sample list:
ls = ['hello','world \n','my name','is john \n','How are you?','I am \n doing well']
ls
return/tryouts:
print([s for s in ls if "\n" in s[-1]])
>>> ['world \n', 'is john \n'] # gave elements that ends with \n
How do I get it elements that ends with '\n' merge with 1 before element? Looking for a output like this one:
['hello world \n', 'my name is john \n', 'How are you?','I am \n doing well']
If you are reducing a list, maybe, one readable approach is to use reduce function.
functools.reduce(func, iter, [initial_value]) cumulatively performs an operation on all the iterable’s elements and, therefore, can’t be applied to infinite iterables.
First of all, you need a kind of struck to accumulate results, I use a tuple with two elements: buffer with concatenated strings until I found "\n" and the list of results. See initial struct (1).
ls = ['hello','world \n','my name','is john \n','How are you?','I am \n doing well']
def combine(x,y):
if y.endswith('\n'):
return ( "", x[1]+[x[0]+" "+y] ) #<-- buffer to list
else:
return ( x[0]+" "+y, x[1] ) #<-- on buffer
t=reduce( combine, ls, ("",[]) ) #<-- see initial struct (1)
t[1]+[t[0]] if t[0] else t[1] #<-- add buffer if not empty
Result:
['hello world \n', 'my name is john \n', 'How are you? ', 'I am \n doing well ']
(1) Explained initial struct: you use a tuple to store buffer string until \n and a list of already cooked strings:
("",[])
Means:
("__ buffer string not yet added to list __", [ __result list ___ ] )
I wrote this out so it is simple to understand instead of trying to make it more complex as a list comprehension.
This will work for any number of words until you hit a \n character and clean up the remainder of your input as well.
ls_out = [] # your outgoing ls
out = '' # keeps your words to use
for i in range(0, len(ls)):
if '\n' in ls[i]: # check for the ending word, if so, add it to output and reset
out += ls[i]
ls_out.append(out)
out = ''
else: # otherwise add to your current word list
out += ls[i]
if out: # check for remaining words in out if total ls doesn't end with \n
ls_out.append(out)
You may need to add spaces when you string concatenate but I am guessing that it is just with your example. If you do, make this edit:
out += ' ' + ls[i]
Edit:
If you want to only grab the one before and not multiple before, you could do this:
ls_out = []
for i in range(0, len(ls)):
if ls[i].endswith('\n'): # check ending only
if not ls[i-1].endswith('\n'): # check previous string
out = ls[i-1] + ' ' + ls[i] # concatenate together
else:
out = ls[i] # this one does, previous didn't
elif ls[i+1].endswith('\n'): # next one will grab this so skip
continue
else:
out = ls[i] # next one won't so add this one in
ls_out.append(out)
You can solve it using the regex expression using the 're' module.
import re
ls = ['hello','world \n','my name','is john \n','How are you?','I am \n doing well']
new_ls = []
for i in range(len(ls)):
concat_word = '' # reset the concat word to ''
if re.search(r"\n$", str(ls[i])): # matching the \n at the end of the word
try:
concat_word = str(ls[i-1]) + ' ' + str(ls[i]) # appending to the previous word
except:
concat_word = str(ls[i]) # in case if the first word in the list has \n
new_ls.append(concat_word)
elif re.search(r'\n',str(ls[i])): # matching the \n anywhere in the word
concat_word = str(ls[i])
new_ls.extend([str(ls[i-1]), concat_word]) # keeps the word before the "anywhere" match separate
print(new_ls)
This returns the output
['hello world \n', 'my name is john \n', 'How are you?', 'I am \n doing well']
Assuming the first element doesn't end with \n and all words are longer than 2 characters:
res = []
for el in ls:
if el[-2:] == "\n":
res[-1] = res[-1] + el
else:
res.append(el)
Try this:
lst=[]
for i in range(len(ls)):
if "\n" in ls[i][-1]:
lst.append((ls[i-1] + ' ' + ls[i]))
lst.remove(ls[i-1])
else:
lst.append(ls[i])
lst
Result:
['hello world \n', 'my name is john \n', 'How are you?', 'I am \n doing well']

I need a small hint... 'string index out of range python module'

I know that it is very weird code, but try not to pay attention. I just want to solve this task with such a strange method. But in the process, I am faced with this problem. Can you help me to fix it?
in <module>
in reverse_alternate
IndexError: string index out of range
I suppose that it's associated with modulo. Right?
def reverse_alternate(string):
a = string.split(' ')
new = ''
for i, c in enumerate(a):
if i % 2 != 0:
new += ' ' + c[::-1] + ' '
else:
new += c
if new[-1] == ' ':
a = new[:-1]
return a
else:
return new
Replace
if new[-1] == ' ':
with
if len(new) and new[-1] == ' ':
If you have no tokens, new will end up being empty, and as such, it won't have the -1'st element. Thus, referencing it would result in "index out of range" error.

Joining words together with a comma, and "and"

I'm working through 'Automate the Boring Stuff with Python'. I can't figure out how to remove the final output comma from the program below. The goal is to keep prompting the user to input values, which are then printed out in a list, with "and" inserted before the end. The output should look something like this:
apples, bananas, tofu, and cats
Mine looks like this:
apples, bananas, tofu, and cats,
That last comma is driving me NUTS.
def lister():
listed = []
while True:
print('type what you want to be listed or type nothing to exit')
inputted = input()
if inputted == '':
break
else:
listed.append(inputted+',')
listed.insert(-1, 'and')
for i in listed:
print(i, end=' ')
lister()
You can avoid adding commas to each string in the list by deferring the formatting to print time. Join all the items excluding the last on ', ', then use formatting to insert the joined string with the last item conjuncted by and:
listed.append(inputed)
...
print('{}, and {}'.format(', '.join(listed[:-1]), listed[-1]))
Demo:
>>> listed = ['a', 'b', 'c', 'd']
>>> print('{}, and {}'.format(', '.join(listed[:-1]), listed[-1]))
a, b, c, and d
The accepted answer is good, but it might be better to move this functionality into a separate function that takes a list, and also handle the edge cases of 0, 1, or 2 items in the list:
def oxfordcomma(listed):
if len(listed) == 0:
return ''
if len(listed) == 1:
return listed[0]
if len(listed) == 2:
return listed[0] + ' and ' + listed[1]
return ', '.join(listed[:-1]) + ', and ' + listed[-1]
Test cases:
>>> oxfordcomma([])
''
>>> oxfordcomma(['apples'])
'apples'
>>> oxfordcomma(['apples', 'pears'])
'apples and pears'
>>> oxfordcomma(['apples', 'pears', 'grapes'])
'apples, pears, and grapes'
This will remove the comma from the last word.
listed[-1] = listed[-1][:-1]
The way it works is listed[-1] gets the last value from the list. We use = to assign this value to listed[-1][:-1], which is a slice of the last word from the list with everything before the last character.
Implemented as shown below:
def lister():
listed = []
while True:
print('type what you want to be listed or type nothing to exit')
inputted = input()
if inputted == '':
break
else:
listed.append(inputted+',')
listed.insert(-1, 'and')
listed[-1] = listed[-1][:-1]
for i in listed:
print(i, end=' ')
lister()
Modifying your code a little bit...
def lister():
listed = []
while True:
print('type what you want to be listed or type nothing to exit')
inputted = input()
if inputted == '':
break
else:
listed.append(inputted) # removed the comma here
print(', '.join(listed[:-2]) + ' and ' + listed[-1]) #using the join operator, and appending and xxx at the end
lister()
listed[-1] = listed[-1][:-1]
This will truncate the final character of the final string in listed.
Lots of ways to do it, but how about this?
# listed[-1] is the last element of the list
# rstrip removes matching characters from the end of the string
listed[-1] = listed[-1].rstrip(',')
listed.insert(-1, 'and')
for i in listed:
print(i, end=' ')
You'll still be printing a space at the end of the line, but I guess you won't see it and thus won't care. :-)
I would do it using an f-string (a formatted string literal, available in Python 3.6+):
def grammatically_join(words, oxford_comma=False):
if len(words) == 0:
return ""
if len(words) == 1:
return listed[0]
if len(words) == 2:
return f"{listed[0]} and {listed[1]}"
return f'{", ".join(words[:-1])}{"," if oxford_comma else ""} and {words[-1]}'
If you don't need the Oxford comma, then you can simplify the code and remove the extra edge case for len(words) == 2:
def grammatically_join(words):
if len(words) == 0:
return ""
if len(words) == 1:
return listed[0]
return f'{", ".join(words[:-1])} and {words[-1]}'
Assuming you're okay with a comma if there are only two items, this is fairly compact:
def commaize(items):
return ', and'.join(', '.join(items).rsplit(',', 1))
Which behaves like this:
>>> commaize([])
''
>>> commaize(['apples'])
'apples'
>>> commaize(['apples', 'bananas'])
'apples, and bananas'
>>> commaize(['apples', 'bananas', 'tofu', 'cats'])
'apples, bananas, tofu, and cats'

My WriteToFile function is not saving anything despite not coming up with any errors?

I am new to this platform so excuse any mistakes in this question. I am trying to write a program that allows a teacher to input 30 students names and their marks to 3 tests each out of 20, 25 and 35, respectfully. I need it to save the inputted details afterwards, this is where I am having problems. it is not saving the details.
Below is my 'WriteToFile' function:
def WriteFile(Names, MarkTest1, MarkTest2, MarkTest3):
FileName = raw_input('Please Enter The File Name: ')
print
WriteFile = open(FileName, 'w')
Lines = ''
for Pupil in range(len(Names)):
Lines = Lines + str(Names[Pupil]) + ', ' + str(MarkTest1[Pupil]) + ', ' + str(MarkTest2[Pupil]) + ', ' + str(MarkTest3[Pupil]) + '\n'
WriteFile.write(Lines)
WriteFile.close()
Below is part of the program that inputs and saves the details:
if Choice in 'Ww':
for P in range(Pupils):
Name = getName("Please Enter The Students Name: ")
print
Mark1 = getMark("Please Enter The Students Mark For Test 1: ", 20)
print
Mark2 = getMark("Please Enter The Students Mark For Test 2: ", 25)
print
Mark3 = getMark("Please Enter The Students Mark For Test 3: ", 35)
print
Names.append(Name)
MarkTest1.append(Mark1)
MarkTest2.append(Mark2)
MarkTest3.append(Mark3)
WriteFile(Names, MarkTest1, MarkTest2, MarkTest3)
getContinueChoice()
Hope someone can help, Thanks.
IndexError: string index out of range occurs when you try to access a character of string beyond its length among many other reasons.
ex.
a = 'Hello, World!'
print a[0]
print a[42] # This will cause IndexError
b='24'
print b[1] # prints 2
print b[2] # IndexError
In your case, You are trying to access MarkTest1[Pupil].
I see MarkTest1 is Mark1 from the calling function. Which is basically user input.
I am assuming length of Names is greater than this string input. Lets say length of Names is 3. Mark1 is '24' (see this is a string).
In the loop
for Pupil in range(len(Names)):
...
Pupil assumes [0,1,2]
And, when you access MarkTest1[2] which is basically '24'[2] or b[2] in my example.
So, you are getting IndexError.
Now What do you do:
From here you need to debug. See what exactly is each variable before the the error occurs by printing them. Gives you an insight. And, lot of learning.
Have fun debugging :)

concatenate print stmt inside and outside of for loop into a sentence in python

def PrintFruiteListSentence(list_of_fruits):
print 'You would like to eat',
for i, item in enumerate (list_of_fruits):
if i != (len(list_of_fruits) - 1):
print item, 'as fruit', i+2, 'and',
else:
print item, 'as fruit', i+2,
print 'in your diet'
o/p
You would like to eat apple as fruit 1 and orange as fruit 2 and banana as fruit 3 and grape as fruit 4 in your diet.
How can i get this sentence in a variable which i can pass to another function ???
I want to pass this sentence as input to another function.
just change your call to print instead to a concatenation into an actual string.
def PrintFruiteListSentence(list_of_fruits):
sentence = 'You would like to eat '
for i, item in enumerate (list_of_fruits):
if i != (len(list_of_fruits) - 1):
sentence += item + ' as fruit ' + str(i+2) + ' and '
else:
sentence += item + ' as fruit ' + str(i+2)
sentence += ' in your diet'
print sentence
you could also use a list comprehension instead of a for loop but this is just unnecessary:
Also note that if you want i to start at a specific number, you can pass an index into enumerate
>>> def PrintFruiteListSentence(list_of_fruits):
sentence = 'You would like to eat ' + ' and '.join(fruit + ' as fruit ' + str(index) for index,fruit in enumerate(list_of_fruits,1)) + ' in your diet'
print(sentence)
>>> PrintFruiteListSentence(['apple','orange','grapes'])
You would like to eat apple as fruit 1 and orange as fruit 2 and grapes as fruit 3 in your diet
EDIT: make sure to convert i+2 to str(i+2)
The following code works:
def func(fruits):
start = "You would like to eat "
for i, item in enumerate(fruits):
if i != (len(fruits) - 1):
start += item + ' as fruit ' + str(i+1) + ' and ' # note you mistake(i+1 and not i+2)
else:
start += item + ' as fruit ' + str(i+1) # see above comment note
start += ' in your diet'
return start
print (func(["apple", "banana", "grapes"]))
You can also try to run the above snippet here on repl.it
First, you need to make it a variable, like so:
def PrintFruiteListSentence(list_of_fruits):
myStr = 'You would like to eat ',
for i, item in enumerate (list_of_fruits):
if i != (len(list_of_fruits) - 1):
myStr += item + ' as fruit ' + str(i+2)+ ' and '
else:
myStr += item + ' as fruit ' + str(i+2)
myStr += ' in your diet'
return myStr
def otherFunction(inputString):
print(inputString)
otherFunction(PrintFruiteListSentence(['apple','banana']))#example
Also look at str.format(), which makes life much easier.
EDIT:
Here is an example to illustrate a simple usage of str.format(). It might not seem powerful in this case, but can be very helpful for complicated string manipulation, or where you need a specific floating point format.
def formatExample(list_of_fruits):
myStr="you would like to eat "
for i in enumerate(list_of_fruits,1):
myStr += '{1:} as fruit {0:d}'.format(*i)+' and '
return myStr[:-4]+"in your diet."
otherFunction(formatExample(['apple','banana']))#prints the same thing
Okay this question has been answered already. But here is another take where the str.join method takes the center stage it deserves. No concatenating strings with +. No if / else statements. No unnecessary variables. Easy to read and understand whats happening:
def PrintFruiteListSentence(fruits):
return ' '.join([
'You would like to eat',
' and '.join(
'{0} as fruit {1}'.format(f, c) for c, f in enumerate(fruits, 1)
),
'in your diet'
])

Categories

Resources