Related
Imagine I have a dictionary as such:
barcodedict={"12_20":[10,15,20], "12_21":[5, "5_1","5_2",6]}
Then I have a number that corresponds to a date, lets say 12_21 and we append it to the values of this date if it is not there as such:
if 8 not in barcodedict["12_21"]:
barcodedict["12_21"].append(8)
{'12_20': [10, 15, 20], '12_21': [5, "5_1", "5_2", 6, 8]}
However, if this number is already present in the value list, I want to add it to the value list with an extra integer that states that its a new occurrence as such:
if 5 not in barcodedict["12_21"]:
barcodedict["12_21"].append(5)
else: #which is now the case
barcodedict["12_21"].append(5_(2+1))
Desired output:
{"12_20":[10,15,20], "12_21":[5, "5_1","5_2","5_3",6, 8]}
As can be seen from the second example, I am not allowed to put underscore in list numbers and they are removed (5_1 becomes 51). And how can I achieve adding a new listing with +1 to the last number? I tried iterating over them and then splitting them but this seems unpythonic and didn't work because the underscore is ignored.
Edit 7/19/2022 10:46AM,
I found a bit of a hackish way around but it seems to hold for now:
placeholder=[]
for i in barcodedict["12_21"]:
if "5" in str(i):
try:
placeholder.append(str(i).split("_")[1])
except:
print("this is for the first 5 occurence, that has no _notation")
print(placeholder)
if len(placeholder) == 0 :
placeholder=[0]
occurence=max(list(map(int, placeholder)))+1
barcodedict["12_21"].append("5_"+occurence)
prints {'12_20': [10, 15, 20], '12_21': [5, '5_1', '5_2', 6, '5_3']}
With the requested number/string mixture it can be done with:
if 5 not in barcodedict["12_21"]:
barcodedict["12_21"].append(5)
else: #which is now the case
i = 1
while True:
if f"5_{i}" not in barcodedict["12_21"]:
barcodedict["12_21"].append(f"5_{i}")
break
i += 1
Underscores used like that do not show up in print, because they are meant to be used for convenience in representing big numbers, but when interpreted they don't show like that. You should use string manipulation if the way they're are displayed matters, or the other way around if you want to actually use them as numbers and want simply to represent them in a convenient way.
Another solution:
def fancy_append(dct, key, val):
last_num = max(
(
int(s[1])
for v in dct[key]
if isinstance(v, str) and (s := v.split("_"))[0] == str(val)
),
default=0,
)
dct[key].append(f"{val}_{last_num+1}" if last_num > 0 else val)
barcodedict = {"12_20": [10, 15, 20], "12_21": [5, "5_1", "5_2", 6]}
fancy_append(barcodedict, "12_21", 5)
print(barcodedict)
Prints:
{'12_20': [10, 15, 20], '12_21': [5, '5_1', '5_2', 6, '5_3']}
I'm writing a code that returns the elements on odd positions in that list. When I run my program, it's showing no output. But when I replace return lst2 with return print(lst2) it's showing me the required output.
So my question is why my program is not showing any output when I write return lst2?
def op(lst):
lst2=[]
for i in lst:
indx=lst.index(i)
if indx%2==1:
lst2.append(i)
return lst2
a=[22, 5, 7, 35, 1, 100, 15]
op(a)
You are just returning the list, not printing it.
def op(lst):
lst2 = []
for i in lst:
indx = lst.index(i)
if indx % 2 == 1:
lst2.append(i)
return lst2
a = [22, 5, 7, 35, 1, 100, 15]
print(op(a))
Because you are not printing the list, you are just returning it (the list object itself). This op(a) is appropriate if you want to do something with the list, but if you just want to print its content you should call print(op(a)) or change the op function to print the contents of the list instead of returning it.
I have a project wherein you need to read data from an excel file. I use openpyxl to read the said file. I tried reading the data as string first before converting it to an integer; however, error is occurring because of, I think, numbers in one cell separated by comma. I am trying to do a nested list but I still new in Python.
My code looks like this:
# storing S
S_follow = []
for row in range(2, max_row+1):
if (sheet.cell(row,3).value is not None):
S_follow.append(sheet.cell(row, 3).value);
# to convert the list from string to int, nested list
for i in range(0, len(S_follow)):
S_follow[i] = int(S_follow[i])
print(S_follow)
The data I a trying to read is:
['2,3', 4, '5,6', 8, 7, 9, 8, 9, 3, 11, 0]
hoping for your help
When you're about to convert the values to integers in the loop on the second-last line of your script, you can check if each value is an integer or string and if it is a string, just split it, convert the split values to integers and push them to a temporary list called say, strVal and then append that temp list to a new list called, say S_follow_int. But if the value is not a string, then just append them to S_follow_int without doing anything.
data= ['2,3', 4, '5,6', 8, 7, 9, 8, 9, 3, 11, 0]
S_follow = []
S_follow_int = []
for row in range(0, len(data)):
if (sheet.cell(row,3).value is not None):
S_follow.append(sheet.cell(row, 3).value);
# to convert the list from string to int, nested list
for i in range(0, len(S_follow)):
#if the current value is a string, split it, convert the values to integers, put them on a temp list called strVal and then append it to S_follow_int
if type(S_follow[i]) is str:
x = S_follow[i].split(',')
strVal = []
for y in x:
strVal.append(int(y))
S_follow_int.append(strVal)
#else if it is already an integer, just append it to S_follow_int without doing anything
else:
S_follow_int.append(S_follow[i])
print(S_follow_int)
However, I would recommend that you check the datatype(str/int) of each value in the initial loop that you used to retrieved data from the excel file itself rather than pushing all values to S_follow and then convert the type afterwards like this:
#simplified representation of the logic you can use for your script
data = ['2,3', 4, '5,6', 8, 7, 9, 8, 9, 3, 11, 0]
x = []
for dat in data:
if dat is not None:
if type(dat) is str:
y = dat.split(',')
strVal = []
for z in y:
strVal.append(int(z))
x.append(strVal)
else:
x.append(dat)
print(x)
S_follow = ['2,3', 4, '5,6', 8, 7, 9, 8, 9, 3, 11, 0]
for i in range(0, len(S_follow)):
try:
s = S_follow[i].split(',')
del S_follow[i]
for j in range(len(s)):
s[j] = int(s[j])
S_follow.insert(i,s)
except AttributeError as e:
S_follow[i] = int(S_follow[i])
print(S_follow)
Can you help me with this homework please?
You are required to complete the function unique_list(l). where "l" is a list of numbers. the function is expected to return the unique numbers in that list.
Example:
(input : [1,1,1,2,2,3,3,3enter code here,4,5,5,6])
output: [1,2,3,4,5,6]
you are not allowed to change the variable names or their values or edit any other code, except the function's body, doing so may jeopardize your evaluation
//no_list = [22,22,2,1,11,11,2,2,3,3,3,4,5,5,5,55,55,66]
//def unique_list(l): //code should be here
//print(unique_list(no_list))
This code pick out the unique elements without changing the order:
no_list = [22,22,2,1,11,11,2,2,3,3,3,4,5,5,5,55,55,66]
def unique_list(l): //code should be here
return list({}.fromkeys(l).keys())
print(unique_list(no_list)) # [22, 2, 1, 11, 3, 4, 5, 55, 66]
Following code will give the unique numbers in a list without changing the order.
>> def unique_list(l):
... final_list = []
... for num in l:
... if num not in final_list:
... final_list.append(num)
... return final_list
...
>>> print (unique_list(no_list))
[22, 2, 1, 11, 3, 4, 5, 55, 66]
Look up the set function and put it back into a list.
You can use a set operation.
no_list = [22,22,2,1,11,11,2,2,3,3,3,4,5,5,5,55,55,66]
set(no_list)
[22,2,1,11,3,4,5,55,66]
You are required to complete the function average(x). where "x" is a list of numbers, and contains more than 3 numbers. the function is expected to return the average from that list.
You can change the numbers in the list no_list but you are not allowed to change the variable names or edit any other code, except the function's body, doing so may jeopardize your evaluation
no_list = [22,68,90,78,90,88]
def average(x):
#complete the function's body to return the average
print(average(no_list))
I am after a string format to efficiently represent a set of indices.
For example "1-3,6,8-10,16" would produce [1,2,3,6,8,9,10,16]
Ideally I would also be able to represent infinite sequences.
Is there an existing standard way of doing this? Or a good library? Or can you propose your own format?
thanks!
Edit: Wow! - thanks for all the well considered responses. I agree I should use ':' instead. Any ideas about infinite lists? I was thinking of using "1.." to represent all positive numbers.
The use case is for a shopping cart. For some products I need to restrict product sales to multiples of X, for others any positive number. So I am after a string format to represent this in the database.
You don't need a string for that, This is as simple as it can get:
from types import SliceType
class sequence(object):
def __getitem__(self, item):
for a in item:
if isinstance(a, SliceType):
i = a.start
step = a.step if a.step else 1
while True:
if a.stop and i > a.stop:
break
yield i
i += step
else:
yield a
print list(sequence()[1:3,6,8:10,16])
Output:
[1, 2, 3, 6, 8, 9, 10, 16]
I'm using Python slice type power to express the sequence ranges. I'm also using generators to be memory efficient.
Please note that I'm adding 1 to the slice stop, otherwise the ranges will be different because the stop in slices is not included.
It supports steps:
>>> list(sequence()[1:3,6,8:20:2])
[1, 2, 3, 6, 8, 10, 12, 14, 16, 18, 20]
And infinite sequences:
sequence()[1:3,6,8:]
1, 2, 3, 6, 8, 9, 10, ...
If you have to give it a string then you can combine #ilya n. parser with this solution. I'll extend #ilya n. parser to support indexes as well as ranges:
def parser(input):
ranges = [a.split('-') for a in input.split(',')]
return [slice(*map(int, a)) if len(a) > 1 else int(a[0]) for a in ranges]
Now you can use it like this:
>>> print list(sequence()[parser('1-3,6,8-10,16')])
[1, 2, 3, 6, 8, 9, 10, 16]
If you're into something Pythonic, I think 1:3,6,8:10,16 would be a better choice, as x:y is a standard notation for index range and the syntax allows you to use this notation on objects. Note that the call
z[1:3,6,8:10,16]
gets translated into
z.__getitem__((slice(1, 3, None), 6, slice(8, 10, None), 16))
Even though this is a TypeError if z is a built-in container, you're free to create the class that will return something reasonable, e.g. as NumPy's arrays.
You might also say that by convention 5: and :5 represent infinite index ranges (this is a bit stretched as Python has no built-in types with negative or infinitely large positive indexes).
And here's the parser (a beautiful one-liner that suffers from slice(16, None, None) glitch described below):
def parse(s):
return [slice(*map(int, x.split(':'))) for x in s.split(',')]
There's one pitfall, however: 8:10 by definition includes only indices 8 and 9 -- without upper bound. If that's unacceptable for your purposes, you certainly need a different format and 1-3,6,8-10,16 looks good to me. The parser then would be
def myslice(start, stop=None, step=None):
return slice(start, (stop if stop is not None else start) + 1, step)
def parse(s):
return [myslice(*map(int, x.split('-'))) for x in s.split(',')]
Update: here's the full parser for a combined format:
from sys import maxsize as INF
def indices(s: 'string with indices list') -> 'indices generator':
for x in s.split(','):
splitter = ':' if (':' in x) or (x[0] == '-') else '-'
ix = x.split(splitter)
start = int(ix[0]) if ix[0] is not '' else -INF
if len(ix) == 1:
stop = start + 1
else:
stop = int(ix[1]) if ix[1] is not '' else INF
step = int(ix[2]) if len(ix) > 2 else 1
for y in range(start, stop + (splitter == '-'), step):
yield y
This handles negative numbers as well, so
print(list(indices('-5, 1:3, 6, 8:15:2, 20-25, 18')))
prints
[-5, 1, 2, 6, 7, 8, 10, 12, 14, 20, 21, 22, 23, 24, 25, 18, 19]
Yet another alternative is to use ... (which Python recognizes as the built-in constant Ellipsis so you can call z[...] if you want) but I think 1,...,3,6, 8,...,10,16 is less readable.
This is probably about as lazily as it can be done, meaning it will be okay for even very large lists:
def makerange(s):
for nums in s.split(","): # whole list comma-delimited
range_ = nums.split("-") # number might have a dash - if not, no big deal
start = int(range_[0])
for i in xrange(start, start + 1 if len(range_) == 1 else int(range_[1]) + 1):
yield i
s = "1-3,6,8-10,16"
print list(makerange(s))
output:
[1, 2, 3, 6, 8, 9, 10, 16]
import sys
class Sequencer(object):
def __getitem__(self, items):
if not isinstance(items, (tuple, list)):
items = [items]
for item in items:
if isinstance(item, slice):
for i in xrange(*item.indices(sys.maxint)):
yield i
else:
yield item
>>> s = Sequencer()
>>> print list(s[1:3,6,8:10,16])
[1, 2, 6, 8, 9, 16]
Note that I am using the xrange builtin to generate the sequence. That seems awkward at first because it doesn't include the upper number of sequences by default, however it proves to be very convenient. You can do things like:
>>> print list(s[1:10:3,5,5,16,13:5:-1])
[1, 4, 7, 5, 5, 16, 13, 12, 11, 10, 9, 8, 7, 6]
Which means you can use the step part of xrange.
This looked like a fun puzzle to go with my coffee this morning. If you settle on your given syntax (which looks okay to me, with some notes at the end), here is a pyparsing converter that will take your input string and return a list of integers:
from pyparsing import *
integer = Word(nums).setParseAction(lambda t : int(t[0]))
intrange = integer("start") + '-' + integer("end")
def validateRange(tokens):
if tokens.from_ > tokens.to:
raise Exception("invalid range, start must be <= end")
intrange.setParseAction(validateRange)
intrange.addParseAction(lambda t: list(range(t.start, t.end+1)))
indices = delimitedList(intrange | integer)
def mergeRanges(tokens):
ret = set()
for item in tokens:
if isinstance(item,int):
ret.add(item)
else:
ret += set(item)
return sorted(ret)
indices.setParseAction(mergeRanges)
test = "1-3,6,8-10,16"
print indices.parseString(test)
This also takes care of any overlapping or duplicate entries, such "3-8,4,6,3,4", and returns a list of just the unique integers.
The parser takes care of validating that ranges like "10-3" are not allowed. If you really wanted to allow this, and have something like "1,5-3,7" return 1,5,4,3,7, then you could tweak the intrange and mergeRanges parse actions to get this simpler result (and discard the validateRange parse action altogether).
You are very likely to get whitespace in your expressions, I assume that this is not significant. "1, 2, 3-6" would be handled the same as "1,2,3-6". Pyparsing does this by default, so you don't see any special whitespace handling in the code above (but it's there...)
This parser does not handle negative indices, but if that were needed too, just change the definition of integer to:
integer = Combine(Optional('-') + Word(nums)).setParseAction(lambda t : int(t[0]))
Your example didn't list any negatives, so I left it out for now.
Python uses ':' for a ranging delimiter, so your original string could have looked like "1:3,6,8:10,16", and Pascal used '..' for array ranges, giving "1..3,6,8..10,16" - meh, dashes are just as good as far as I'm concerned.