Interpreting Number Ranges in Python - python

In a Pylons webapp, I need to take a string such as "<3, 45, 46, 48-51, 77" and create a list of ints (which are actually IDs of objects) to search on.
Any suggestions on ways to do this? I'm new to Python, and I haven't found anything out there that helps with this kind of thing.
The list would be: [1, 2, 3, 45, 46, 48, 49, 50, 51, 77]

Use parseIntSet from here
I also like the pyparsing implementation in the comments at the end.
The parseIntSet has been modified here to handle "<3"-type entries and to only spit out the invalid strings if there are any.
#! /usr/local/bin/python
import sys
import os
# return a set of selected values when a string in the form:
# 1-4,6
# would return:
# 1,2,3,4,6
# as expected...
def parseIntSet(nputstr=""):
selection = set()
invalid = set()
# tokens are comma seperated values
tokens = [x.strip() for x in nputstr.split(',')]
for i in tokens:
if len(i) > 0:
if i[:1] == "<":
i = "1-%s"%(i[1:])
try:
# typically tokens are plain old integers
selection.add(int(i))
except:
# if not, then it might be a range
try:
token = [int(k.strip()) for k in i.split('-')]
if len(token) > 1:
token.sort()
# we have items seperated by a dash
# try to build a valid range
first = token[0]
last = token[len(token)-1]
for x in range(first, last+1):
selection.add(x)
except:
# not an int and not a range...
invalid.add(i)
# Report invalid tokens before returning valid selection
if len(invalid) > 0:
print "Invalid set: " + str(invalid)
return selection
# end parseIntSet
print 'Generate a list of selected items!'
nputstr = raw_input('Enter a list of items: ')
selection = parseIntSet(nputstr)
print 'Your selection is: '
print str(selection)
And here's the output from the sample run:
$ python qq.py
Generate a list of selected items!
Enter a list of items: <3, 45, 46, 48-51, 77
Your selection is:
set([1, 2, 3, 45, 46, 77, 48, 49, 50, 51])

I've created a version of #vartec's solution which I feel is more readable:
def _parse_range(numbers: str):
for x in numbers.split(','):
x = x.strip()
if x.isdigit():
yield int(x)
elif x[0] == '<':
yield from range(0, int(x[1:]))
elif '-' in x:
xr = x.split('-')
yield from range(int(xr[0].strip()), int(xr[1].strip())+1)
else:
raise ValueError(f"Unknown range specified: {x}")
In the process, the function became a generator :)

rng = "<3, 45, 46, 48-51, 77"
ids = []
for x in map(str.strip,rng.split(',')):
if x.isdigit():
ids.append(int(x))
continue
if x[0] == '<':
ids.extend(range(1,int(x[1:])+1))
continue
if '-' in x:
xr = map(str.strip,x.split('-'))
ids.extend(range(int(xr[0]),int(xr[1])+1))
continue
else:
raise Exception, 'unknown range type: "%s"'%x

First, you'll need to figure out what kind of syntax you'll accept. You current have three in your example:
Single number: 45, 46
Less than operator
Dash ranging: 48-51
After that, it's just a matter of splitting the string into tokens, and checking the format of the token.

I also had to do something similar for an app lately.
If you don't need concrete numbers but just a way to see whether a given number is in the range, you might consider parsing it to a Python expression you can eval into a lambda. For example <3, 5-10, 12 could be func=(lambda x:x<3 or (5 <= x <= 10) or x==12)). Then you can just call the lambda, func(11) to see if 11 belongs in there.

>>> print range.__doc__
range([start,] stop[, step]) -> list of integers
Return a list containing an arithmetic progression of integers.
range(i, j) returns [i, i+1, i+2, ..., j-1]; start (!) defaults to 0.
When step is given, it specifies the increment (or decrement).
For example, range(4) returns [0, 1, 2, 3]. The end point is omitted!
These are exactly the valid indices for a list of 4 elements.
>>> range(33,44)
[33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43]
>>> range(1,3)
[1, 2]
I imagine you could iterate your list, and call range appropriately.
>>> def lessThan(n) :
... return range(n+1)
...
>>> lessThan(4)
[0, 1, 2, 3, 4]
>>> def toFrom(n,m):
... return range(n,m)
...
>>> toFrom(33,44)
[33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43]
Then split the string on commas, and for each bit, parse it enough to figure out what function to call, catenating the lists returned.
Anything more and I'd have written it for you.

Related

how to pass the result of one function to another?

i am beginner of python and i have a question. How to pass the result of one
function to another one. I want to make new list based on first one only with prime digits, and when i run my program second list is empty
def get_list_of_int_numbers(n: int):
list1 = [random.randint(10, 100) for x in range(random.randint(10, 100))] * n
return list1
def get_prime_digits(n: int) -> bool:
return n in [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37,
41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97.]
def get_second_list_based_on_old_one_with_prime_digits(expression: list) -> list:
list2 = [n for n in expression if n == get_prime_digits(n)]
return list2
def main() -> None:
n1 = random.randint(10, 100)
print(get_list_of_int_numbers(n1))
print(get_second_list_based_on_old_one_with_prime_digits(get_list_of_int_numbers(n1)))
if __name__ == '__main__':
main()
any help would be appreciated
Problem is in your get_second_list_based_on_old_one_with_prime_digits, you don't need to use n to compare with the result of get_prime_digits
def get_second_list_based_on_old_one_with_prime_digits(expression: list) -> list:
list2 = [n for n in expression if n == get_prime_digits(n)]
# ^^^^
return list2
Well, I don't know why the second printed list would always be empty but it will certainly not be using the values created in your first call to get_list_of_int_numbers(...).
This is because you are not saving the results of the first call to get_list_of_int_numbers(...) into a variable. And the second call to it, creates a different list.
def main() -> None:
n1 = random.randint(10, 100)
new_list : list = get_list_of_int_numbers(n1) # Save first list here
print(get_second_list_based_on_old_one_with_prime_digits(new_list)) # Reuse first list
There are two main issues with your code.
First, you are not saving the result of your first call to get_list_of_int_numbers .
Second, your get_second_list_based_on_old_one_with_prime_digits function compares n == get_prime_digits(n), which will always be false b/c get_prime_digits returns a bool.
Something like:
def get_second_list_based_on_old_one_with_prime_digits(expression: list) -> list:
list2 = [n for n in expression if get_prime_digits(n)]
return list2
def main() -> None:
n1 = random.randint(10, 100)
n1_list = get_list_of_int_numbers(n1)
print(n1_list)
print(get_second_list_based_on_old_one_with_prime_digits(n1_list))
should work better for you.

Given a list of numbers, how can I remove all multiples of primes (but not primes)?

I am starting on my Python journey and am doing some exercises to get the hang of it all. One question is really giving me troubles as I do not understand how to complete it.
Question:
Given a list of natural numbers, remove from it all multiples of 2 (but not 2), multiples of 3 (but not 3), and so on, up to the multiples of 100, and print the remaining values.
From this question I take it that I should first make a list with all prime numbers and after that append the values that correspond to a new list. This is what I have until know:
# Read list:
a = [5, 6, 7, 8, 9]
# First get a list with all primes
primes = []
for i in range(0, 101):
for j in range(2, int(i ** 0.5) + 1):
if i%j == 0:
break
else:
primes.append(i)
# Next append to new list
b = []
for num in a:
for prime in primes:
if num == prime and num % prime == 0:
b.append(num)
print(b)
Now this works for this simple example, but upon testing it on another set (one of which I do not know the input values of the test case), my output is:
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
Which is my original list with the prime numbers. So somewhere along the line I am making a mistake and I cannot see the logic behind it. Any help would be greatly appreciated.
To solve the task all you need to do is filter out the numbers by using a generator and a for loop, which by itself is already finding the prime numbers
a = [5,6,7,8,9]
for i in range(2,101):
a = [j for j in a if (j==i or j%i!=0)]

Writing list with criteria 1 line - Python

I am trying to get better at writing more 'clean' and/or elegant code. I have seen examples around but I can't make them work for this particular example. Logic is very simple and I'm hoping that with some pointers on punctuation or syntax I can pick up the habit.
a = [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
elist = []
evenlist = [i for i in a if a % 2 == 0]
print(evenlist)
I have only been able to make this work on the longer format here below:
a = [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
elist = []
for i in a:
if i % 2 == 0:
elist.append(i)
print(elist)
Should be this:
evenlist = [i for i in a if i % 2 == 0]
evenlist = [i for i in a if a % 2 == 0]
# ^
# Hmmm!
You probably wat to be checking i (an element) for evenness, rather than a (the entire list). Checking a list for evenness doesn't make much sense unless there's some magic happening in the background that ensures every element in the list is even. But there's not:
>>> [1,2,3] % 2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for %: 'list' and 'int'
As an aside, there's another way to do this, specifically:
list(filter(lambda x: x % 2 == 0, a))
I think this is less readable myself but I suspect it may be more space-efficient if you just want to process them rather than create a list from them:
for item in filter(lambda x: x % 2 == 0, a):
do_something_with(item)
That doesn't create a whole new list, instead simply retrieving values from the existing list that match the filter criteria. Probably won't matter unless your lists tend to get large.

Perform operation on every array element pair quickly

I have an array A
A = [5,2,8,14,6,13]
I want to get an array where each element is added to every other element, so the first five elements would be 5 + each element, then the next four would be 2 + each element etc.
So the result would be
B = [7,13,19,11,18, 10,16,8,15, 22,14,21, 20,27, 19]
What is the quickest way to do this without using for loops?
Note: The problem I am trying to solve involves large boolean arrays instead of integers and the actual operation is a boolean 'and', not merely addition. I have simplified the question for ease of explanation. I have been using for loops up to now, but I am looking for a faster alternative.
Use ` itertools.combinations
from itertools import combinations
a = [5,2,8,14,6,13]
print [sum(i) for i in list(combinations(a, 2))]
No need of list(). Thanks to #PeterWood
print [sum(i) for i in combinations(a, 2)]
Output:
[7, 13, 19, 11, 18, 10, 16, 8, 15, 22, 14, 21, 20, 27, 19]
Demo
You could do it recursively:
def add_value_to_rest(sequence):
if not sequence:
return []
else:
additional = sequence[0]
return ([additional + value for value in sequence] +
add_value_to_rest(sequence[1:]))
With generators, in Python 3:
def add_value_to_rest(sequence):
if sequence:
additional = sequence[0]
for value in sequence:
yield additional + value
yield from add_value_to_rest(sequence[1:])
Or with Python 2.7:
def add_value_to_rest(sequence):
if sequence:
additional = sequence[0]
for value in sequence:
yield additional + value
for value in add_value_to_rest(sequence[1:]):
yield value
A = [5,2,8,14,6,13]
B = []
for i, x in enumerate(A):
for l in range(len(A) - i - 1):
B.append(A[i] + A[i + l + 1])
print B
#[7, 13, 19, 11, 18, 10, 16, 8, 15, 22, 14, 21, 20, 27, 19]

Boolean check not working in function

This code will be part of a program that will check if a number is prime or not. I know it's not particularly elegant, but I want to get it working simply for experience. I think that the function is failing because the logic on the if/elif is wrong, when I run this code, it seems to just go straight to the else clause. Is this a syntax problem, or am I not allowed to do logic checks in if clauses?
list = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]
def find_prime(list, n):
if n in list == False:
list.append(n)
print "I'ts in there now."
elif n in list == True:
print "It's in there already."
else:
print "Error"
find_prime(list, 3)
find_prime(list, 51)
list is a bad name for a variable. It masks the built-in list.
if n in list == True: doesn't do what you await: 1 in [0, 1] == True returns False (because, as #Duncan notes, 1 in [0,1] == True is shorthand for 1 in [0,1] and [0,1] == True). Use if n in li: and if n not in li:
No reason for an Error, since an element is in the list or it is not in the list. Anything else is programming error.
So your code could look like this:
li = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]
def find_prime(li, n):
if n in li:
print "It's in there already."
else:
li.append(n)
print "It's in there now."
Don't call your list list. Call it mylist or something else.
Use if not n in mylist and if n in mylist.
Since the value is either going to be in the list or not, I don't think you need to check three options in your if/else logic.
list = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]
def find_prime(list, n):
if n in list:
print "It's in there already."
else:
list.append(n)
print "It's in there now."
find_prime(list,3)
find_prime(list,53)
Try this code instead of testing for True/False. Also see my comment above regarding using list as a variable name (bad idea since that identifier is used by Python).
mylist = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]
def find_prime(mylist, n):
if not n in mylist:
mylist.append(n)
print "I'ts in there now."
else: # n in mylist: has to be the case
print "It's in there already."
You don't need the original last else, your choice is binary, either the number will be in the list or it won't.

Categories