Related
I have a list (fullList) [5, 8, 9, 10, 5, 9, 10, 11, 8, 9, 13, 14, 9, 10, 13, 15, 10, 11, 14, 15] and a value n, - I would like to iterate the first (n) 4 values, then skip 1 number, then iterate the next 4-1 value, and skip 2 numbers, then iterate the next 4-2 numbers and skip 3 numbers, etc... until the list is fully iterated.
My final answer should look like this:
[5,8,9,10,9,10,11,13,14,15]
It can be obtained by taking the first 4 values (5,8,9,10), then skipping 1 count (5), then take the next 3 values (9,10,11) and skipping 2 counts (8,9), then taking the next 2 values (13,14) and skipping 3 counts (9,10,13), taking the next value (15) and skipping 4 counts (10,11,14,15)
--
Edit: I have obtained fullList from iterating the values of a smaller list (listb) [2,3,6,7,8] by adding it against itself. I have solved this part, but still would like to understand the first part (above).
I would like to achieve the following results:
2+3 = 5
2+6 = 8
2+7 = 9
2+8 = 10
3+6 = 9
3+7 = 10
3+8 = 11
6+7 = 13
6+8 = 14
7+8 = 15
Thank you!
You can use itertools.chain
from itertools import chain
x = [5, 8, 9, 10, 5, 9, 10, 11, 8, 9, 13, 14, 9, 10, 13, 15, 10, 11, 14, 15]
x = list(chain.from_iterable(x[4*idx + idx:4*idx + 4] for idx in range(4)))
For your second part, use itertools.combinations
from itertools import combinations
x = [2,3,6,7,8]
for combo in combinations(x, 2):
a, b = combo
print(f"{a} + {b} = {sum(combo)}")
Using a nested list comprehension:
from itertools import chain
full_list = [5, 8, 9, 10, 5, 9, 10, 11, 8, 9, 13, 14, 9, 10, 13, 15, 10, 11, 14, 15]
n = 4
list(chain.from_iterable(x[i:] for i,x in enumerate(
[x for x in [full_list[i*n:i*n+n] for i in range(n+1)]])))
# [5, 8, 9, 10, 9, 10, 11, 13, 14, 15]
The nested list comprehension:
[x for x in [full_list[i*n:i*n+n] for i in range(n+1)]]
groups the list into sublists of n elements. The outer list comprehension then extracts from those sublists, further sublists of the required length using enumerate() to determine the start offset.
Finally itertools.chain.from_iterable() is used to flatten the sublists into a single list as required.
Well, I made it in 2 different ways:
The first one iterates list element by element with 2 separate counters, the skip counter and the array number counter (counter), when the counter gets fill to the n number (counter == n), it gets into a for loop and starts poping out of the main list the elements (skipping the future iterations as you told us you want to do), when the counterSkip gets to 0, it resets all the counters, reduces the n variable by 1 and increments the number of numbers you want to skip by one (skipN) until the array is full iterated
counter = 0
n= 4
skipN = 1
counterSkip = skipN
array = [5, 8, 9, 10, 5, 9, 10, 11, 8, 9, 13, 14, 9, 10, 13, 15, 10, 11, 14, 15]
for index in enumerate(array):
if counter >= n and counterSkip != 0:
for i in range(counterSkip):
array.pop(index[0])
counterSkip -= 1
n -= 1
skipN += 1
counter = 0
counterSkip = skipN
counter += 1
print(array)
#
[5, 8, 9, 10, 9, 10, 11, 13, 14, 15]
And the second way (It's almost identical as one answer here):
Using the extend() method to add elements to a new list setting lower bound of the main list to a var named "index" that updates it's value summing itself to the n var +1 to preserve a continuity in the iteration, and the upper bound of the main list to the index itself minus i, that serves as a accumulator of how many elements you want to skip each iteration.
You can read more about the "extend" method right here: https://www.programiz.com/python-programming/methods/list/extend
newList = []
index = 0
i = 0
n= 4
list = [5, 8, 9, 10, 5, 9, 10, 11, 8, 9, 13, 14, 9, 10, 13, 15, 10, 11, 14, 15]
while(index<len(list)):
newList.extend(list[index:index+(n-i)])
index+=(n+1)
i+=1
print(newList)
#
[5, 8, 9, 10, 9, 10, 11, 13, 14, 15]
Edit: Ignore the second/alternative part of the question - I managed to get the list that I was looking for
lista = [2,3,6,7,8]
count = 0
originalCount = 0
fullList = []
while count < len(lista):
for j in range(len(lista)):
if (count != j):
if (j > count):
fullList.append(lista[count]+lista[j])
elif (count != originalCount):
j+=count
count+=1
OK, since this seems to have turned into a kind of golfing contest, here's mine:
def upper_left(arr, n):
"""Arguments:
arr is an iterator returning a flattened n x n array.
Since no element is selected from the last row, it can be
just for the first n-1 rows.
Returns:
An iterator which produces the flattened upper-left triangle
of arr, excluding the anti-diagonal.
"""
return (z for z, (i, j) in zip(arr, ((i,j)
for i in range(n)
for j in range(n)))
if i + j < n - 1)
print(list(upper_left(
[
5, 8, 9, 10, 5,
9, 10, 11, 8, 9,
13, 14, 9, 10, 13,
15, 10, 11, 14, 15
], 5)))
This zips the array against the corresponding row/column indices, and then selects the elements for which the indices are above and to the left of the anti-diagonal (that is, the diagonal from the lower-left corner to the upper-right corner).
Running the above file produces:
$ python upper_left.py
[5, 8, 9, 10, 9, 10, 11, 13, 14, 15]
It would arguably be easier to combine the selection of elements with the generation of the elements in the first place. But this function composes well. (It's probably worth writing the functions which produce the other triangles. The only difference is the comparison in the last line of the function.
Personally I think using itertools makes it more complex than it needs to be.
This solution should work.
new_list = []
index = 0
i = 0
while(index<len(fullList)):
new_list.extend(fullList[index:index+(n-i)])
index+=(n+1)
i+=1
Edit
fullList = [5, 8, 9, 10, 5, 9, 10, 11, 8, 9, 13, 14, 9, 10, 13, 15, 10, 11, 14, 15]
new_list = []
index = 0
i = 0
n = 4
while(index<len(fullList)):
new_list.extend(fullList[index:index+(n-i)])
index+=(n+1)
i+=1
print(new_list)
#[5, 8, 9, 10, 9, 10, 11, 13, 14, 15]
even = []
odd = []
lst = []
for i in range(1, 21):
lst.append(i)
To find even or odd
for i in lst:
if i % 2 == 0:
even.append(i)
else:
odd.append(i)
print(even, odd)
Is there any pythonic way of doing it using list comprehension, where the output values of 'if and else' condition must be saved in two different lists.
odd=[]
even=[]
[even.append(i) if i%2==0 else odd.append(i) for i in xrange(1,21)]
You can do this using if else.
For a simple solution, you could do something like this:
odds = [i for i in range(1,21) if i % 2 == 0]
even = [i for i in range(1,21) if i % 2 != 0]
Other people have provided some nice one liners.
Think this is what #fourtheye mentioned in his comment..
>>> el,ol = filter(lambda x: x % 2 == 0, range(1, 21)),filter(lambda x: x % 2 != 0, range(1, 21))
>>> el
[2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
>>> ol
[1, 3, 5, 7, 9, 11, 13, 15, 17, 19]
It is a simple solution.
>>> lst = range(1,21)
>>> odd = range(1,21,2)
>>> even = range(2,21,2)
>>> odd
[1, 3, 5, 7, 9, 11, 13, 15, 17, 19]
>>> even
[2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
>>> lst
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
range(start, stop[, step])
This is a versatile function to create lists containing arithmetic progressions. It is most often used in for loops
Hardly pythonic, and slow, but still possible in a single expression involving a couple of list comprehensions and a zip. Something like:
odds, evens = [[y for y in x if y is not None] for x in zip(*[(x, None) if x % 2 else (None, x) for x in range(1, 21)])]
Breaking that down ... we construct a list of tuples where the first item is a number if its odd, and None otherwise; and the second item is a number if it is even, or None otherwise.
[(x, None) if x % 2 else (None, x) for x in range(1, 21)]
We unzip that into two lists of odds and evens ... unfortunately it contains all the Nones as well
zip(*[(x, None) if x % 2 else (None, x) for x in range(1, 21)])
So we filter them out with the [y for y in x if y is not None]
I know that a[end:start:-1] slices a list in a reverse order.
For example
a = range(20)
print a[15:10:-1] # prints [15, ..., 11]
print a[15:0:-1] # prints [15, ..., 1]
but you cannot get to the first element (0 in the example). It seems that -1 is a special value.
print a[15:-1:-1] # prints []
Any ideas?
You can assign your variable to None:
>>> a = range(20)
>>> a[15:None:-1]
[15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
>>>
Omit the end index:
print a[15::-1]
In Python2.x, the simplest solution in terms of number of characters should probably be :
>>> a=range(20)
>>> a[::-1]
[19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
Though i want to point out that if using xrange(), indexing won't work because xrange() gives you an xrange object instead of a list.
>>> a=xrange(20)
>>> a[::-1]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: sequence index must be integer, not 'slice'
After in Python3.x, range() does what xrange() does in Python2.x but also has an improvement accepting indexing change upon the object.
>>> a = range(20)
>>> a[::-1]
range(19, -1, -1)
>>> b=a[::-1]
>>> for i in b:
... print (i)
...
19
18
17
16
15
14
13
12
11
10
9
8
7
6
5
4
3
2
1
0
>>>
the difference between range() and xrange() learned from source: http://pythoncentral.io/how-to-use-pythons-xrange-and-range/
by author: Joey Payne
>>> a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
>>> print a[:6:-1]
[19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7]
>>> a[7] == a[:6:-1][-1]
True
>>> a[1] == a[:0:-1][-1]
True
So as you can see when subsitute a value in start label :end: it will give you from start to end exclusively a[end].
As you can see in here as well:
>>> a[0:2:]
[0, 1]
-1 is the last value in a:
>>> a[len(a)-1] == a[-1]
True
EDIT: begin and end are variables
I never realized this, but a (slightly hacky) solution would be:
>>> a = range(5)
>>> s = 0
>>> e = 3
>>> b = a[s:e]
>>> b.reverse()
>>> print b
[2, 1, 0]
If you use negative indexes you can avoid extra assignments, using only your start and end variables:
a = range(20)
start = 20
for end in range(21):
a[start:-(len(a)+1-end):-1]
The program below is finding prime numbers in a given range. for the noprimes list comprehension part, why do we have 3 parameters in range?
noprimes = [j for i in range(2, 8) for j in range(i*2, 50, i)]
primes = [x for x in range(2, 50) if x not in noprimes]
print prime
and what is i doing there?
See the docs:
range([start], stop[, step])
When comparing it to a for(..; ..; ..) loop e.g. in C the three arguments are used like this:
for(int i = start; i != stop; i += step)
There are also good examples in the docs:
>>> range(10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> range(1, 11)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> range(0, 30, 5)
[0, 5, 10, 15, 20, 25]
>>> range(0, 10, 3)
[0, 3, 6, 9]
>>> range(0, -10, -1)
[0, -1, -2, -3, -4, -5, -6, -7, -8, -9]
>>> range(0)
[]
>>> range(1, 0)
[]
For range(), the basic idea is that it generates a sequence of items for you. See this as reference http://docs.python.org/library/functions.html#range :
format: range([start], stop[, step])
In the meantime here is some basic explanation, easiest example:
range(5)
will generate numbers in the range starting at 0 (default start value) and go up to but not including 5, by increments of 1 (default value), so
In [1]: range(5)
Out[1]: [0, 1, 2, 3, 4]
You can specify additional parameters to range, such as the starting value, the ending value and also the stepvalue. So range(startval, endval, stepval). Notice that endval is not included in the sequence that is generated.
range(0, 5, 1)
is equivalent to
range(5)
To generate all even numbers between 0 and 20 you could do for instance
range(0, 21, 2)
Note that prior to Python 3 range generates a list and xrange generates the number sequence on demand.
In your specific code are using list comprehensions and range. It might be easier to understand the algorithm and the role of the for loops with range by eliminating the list comprehension temporarily to get a clearer idea. List comprehension is a powerful and efficient construct and should definitely be used if you plan on keeping the original code though.
#noprimes = [j for i in range(2, 8) for j in range(i*2, 50, i)]
noprimes = []
for i in range (2, 8):
for j in range (i*2, 50, i):
noprimes.append(j)
# primes = [x for x in range(2, 50) if x not in noprimes]
primes = []
for x in range(2, 50):
if x not in noprimes:
primes.append(x)
Basically you're stepping by i in non-primes to generate multiples of i (any multiple obviously a non-prime). i is in range(2,8) i.e. [2, 3, 4, 5, 6, 7] because for primes till 50 you only need to eliminate multiples of numbers until sqrt(50) which is 7 (approximately).
If a nested list comprehension is confusing, try breaking it down into steps for easier understanding.
>>> [j for i in [2] for j in range(i*2, 50, i)]
[4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48]
>>> [j for i in [3] for j in range(i*2, 50, i)]
[6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48]
BTW, also look around online for better prime number algorithms. This one algorithmically very poor.
The three parameters of range were explained by ThiefMaster. about the code - code looks ok to me. The only problem seems to be the print prime line. maybe you should add
for prime in primes :
print prime
This question already has answers here:
Identify groups of consecutive numbers in a list
(19 answers)
Closed 4 years ago.
I have a list containing data as such:
[1, 2, 3, 4, 7, 8, 10, 11, 12, 13, 14]
I'd like to print out the ranges of consecutive integers:
1-4, 7-8, 10-14
Is there a built-in/fast/efficient way of doing this?
From the docs:
>>> from itertools import groupby
>>> from operator import itemgetter
>>> data = [ 1, 4,5,6, 10, 15,16,17,18, 22, 25,26,27,28]
>>> for k, g in groupby(enumerate(data), lambda (i, x): i-x):
... print map(itemgetter(1), g)
...
[1]
[4, 5, 6]
[10]
[15, 16, 17, 18]
[22]
[25, 26, 27, 28]
You can adapt this fairly easily to get a printed set of ranges.
A short solution that works without additional imports. It accepts any iterable, sorts unsorted inputs, and removes duplicate items:
def ranges(nums):
nums = sorted(set(nums))
gaps = [[s, e] for s, e in zip(nums, nums[1:]) if s+1 < e]
edges = iter(nums[:1] + sum(gaps, []) + nums[-1:])
return list(zip(edges, edges))
Example:
>>> ranges([2, 3, 4, 7, 8, 9, 15])
[(2, 4), (7, 9), (15, 15)]
>>> ranges([-1, 0, 1, 2, 3, 12, 13, 15, 100])
[(-1, 3), (12, 13), (15, 15), (100, 100)]
>>> ranges(range(100))
[(0, 99)]
>>> ranges([0])
[(0, 0)]
>>> ranges([])
[]
This is the same as #dansalmo's solution which I found amazing, albeit a bit hard to read and apply (as it's not given as a function).
Note that it could easily be modified to spit out "traditional" open ranges [start, end), by e.g. altering the return statement:
return [(s, e+1) for s, e in zip(edges, edges)]
This will print exactly as you specified:
>>> nums = [1, 2, 3, 4, 7, 8, 10, 11, 12, 13, 14]
>>> ranges = sum((list(t) for t in zip(nums, nums[1:]) if t[0]+1 != t[1]), [])
>>> iranges = iter(nums[0:1] + ranges + nums[-1:])
>>> print ', '.join([str(n) + '-' + str(next(iranges)) for n in iranges])
1-4, 7-8, 10-14
If the list has any single number ranges, they would be shown as n-n:
>>> nums = [1, 2, 3, 4, 5, 7, 8, 9, 12, 15, 16, 17, 18]
>>> ranges = sum((list(t) for t in zip(nums, nums[1:]) if t[0]+1 != t[1]), [])
>>> iranges = iter(nums[0:1] + ranges + nums[-1:])
>>> print ', '.join([str(n) + '-' + str(next(iranges)) for n in iranges])
1-5, 7-9, 12-12, 15-18
Built-In: No, as far as I'm aware.
You have to run through the array. Start off with putting the first value in a variable and print it, then as long as you keep hitting the next number do nothing but remember the last number in another variable. If the next number is not in line, check the last number remembered versus the first number. If it's the same, do nothing. If it's different, print "-" and the last number. Then put the current value in the first variable and start over.
At the end of the array you run the same routine as if you had hit a number out of line.
I could have written the code, of course, but I don't want to spoil your homework :-)
I had a similar problem and am using the following for a sorted list. It outputs a dictionary with ranges of values listed in a dictionary. The keys separate each run of consecutive numbers and are also the running total of non-sequential items between numbers in sequence.
Your list gives me an output of {0: [1, 4], 1: [7, 8], 2: [10, 14]}
def series_dictf(index_list):
from collections import defaultdict
series_dict = defaultdict(list)
sequence_dict = dict()
list_len = len(index_list)
series_interrupts = 0
for i in range(list_len):
if i == (list_len - 1):
break
position_a = index_list[i]
position_b = index_list[i + 1]
if position_b == (position_a + 1):
sequence_dict[position_a] = (series_interrupts)
sequence_dict[position_b] = (series_interrupts)
if position_b != (position_a + 1):
series_interrupts += 1
for position, series in sequence_dict.items():
series_dict[series].append(position)
for series, position in series_dict.items():
series_dict[series] = [position[0], position[-1]]
return series_dict
Using set operation, the following algorithm can be executed
def get_consecutive_integer_series(integer_list):
integer_list = sorted(integer_list)
start_item = integer_list[0]
end_item = integer_list[-1]
a = set(integer_list) # Set a
b = range(start_item, end_item+1)
# Pick items that are not in range.
c = set(b) - a # Set operation b-a
li = []
start = 0
for i in sorted(c):
end = b.index(i) # Get end point of the list slicing
li.append(b[start:end]) # Slice list using values
start = end + 1 # Increment the start point for next slicing
li.append(b[start:]) # Add the last series
for sliced_list in li:
if not sliced_list:
# list is empty
continue
if len(sliced_list) == 1:
# If only one item found in list
yield sliced_list[0]
else:
yield "{0}-{1}".format(sliced_list[0], sliced_list[-1])
a = [1, 2, 3, 6, 7, 8, 4, 14, 15, 21]
for series in get_consecutive_integer_series(a):
print series
Output for the above list "a"
1-4
6-8
14-15
21
Here is another basic solution without using any module, which is good for interview, generally in the interview they asked without using any modules:
#!/usr/bin/python
def split_list(n):
"""will return the list index"""
return [(x+1) for x,y in zip(n, n[1:]) if y-x != 1]
def get_sub_list(my_list):
"""will split the list base on the index"""
my_index = split_list(my_list)
output = list()
prev = 0
for index in my_index:
new_list = [ x for x in my_list[prev:] if x < index]
output.append(new_list)
prev += len(new_list)
output.append([ x for x in my_list[prev:]])
return output
my_list = [1, 3, 4, 7, 8, 10, 11, 13, 14]
print get_sub_list(my_list)
Output:
[[1], [3, 4], [7, 8], [10, 11], [13, 14]]
You can use collections library which has a class called Counter. Counter can come in handy if trying to poll the no of distinct elements in any iterable
from collections import Counter
data = [ 1, 4,5,6, 10, 15,16,17,18, 22, 25,26,27,28]
cnt=Counter(data)
print(cnt)
the output for this looks like
Counter({1: 1, 4: 1, 5: 1, 6: 1, 10: 1, 15: 1, 16: 1, 17: 1, 18: 1, 22: 1, 25: 1, 26: 1, 27: 1, 28: 1})
which just like any other dictionary can be polled for key values