I am trying to loop through a list but at the same time make reference to the items before so i can compare.
This is my code:
list1=[(1,'a','hii'),(2,'a','byee'),(3,'a','yoo'),(4,'b','laa'),(5,'a','mehh')]
I want to loop through my list1 tuples such that if the second value in the tuple is the same value as the second value in the tuple before (both =='a'), then concat the third item in the tuple.
output i want:
list2=[('a','hii,byee,yoo'),('b','laa'),('a','mehh')]
What i have tried:
for item in list1:
for item2 in list2:
if item[0]==(item2[0]-1) and item[1]==item2[1]:
print item[2]+','+item2[2]
elif item[0] != item2[0]-1:
continue
elif item[0]==(item2[0]-1) and item[1] != item2[1]:
print item[2]
wrong output
hii,byee
byee,yoo
yoo
laa
From the first 2 output, it seems like the loop only looks at the preceding value but not at say 2 or more value preceding. Hence it only joined 2 words together and not 3 which it should have. The output also ends up having repeats.
How do i get around this?
I was making this WAY harder than it needed to be
def combine(inval):
outval = [inval[0]]
for item in inval[1:]:
if item[0] == outval[-1][0] + 1 and item[1] == outval[-1][1]:
outval[-1] = (item[0], item[1], ",".join([outval[-1][2], item[2]]))
continue
outval.append(item)
return [(item[1], item[2]) for item in outval]
And to test it...
list1 = [(1,'a','hii'),(2,'a','byee'),(3,'a','yoo'),(4,'b','laa'),(5,'a','mehh')]
list2 = [(1,'a','hii'),(3,'a','byee'),(4,'a','yoo'),(5,'b','laa'),(6,'a','mehh')]
list3 = [(1,'a','hoo'),(3,'a','byee'),(5,'a','yoo'),(6,'a','laa'),(7,'a','mehh'),(9, 'b', 'nope')]
for l in (list1, list2, list3):
print "IN:", l
print "OUT:", combine(l)
print
OUTPUT
IN: [(1, 'a', 'hii'), (2, 'a', 'byee'), (3, 'a', 'yoo'), (4, 'b', 'laa'), (5, 'a', 'mehh')]
OUT: [('a', 'hii,byee,yoo'), ('b', 'laa'), ('a', 'mehh')]
IN: [(1, 'a', 'hii'), (3, 'a', 'byee'), (4, 'a', 'yoo'), (5, 'b', 'laa'), (6, 'a', 'mehh')]
OUT: [('a', 'hii'), ('a', 'byee,yoo'), ('b', 'laa'), ('a', 'mehh')]
IN: [(1, 'a', 'hoo'), (3, 'a', 'byee'), (5, 'a', 'yoo'), (6, 'a', 'laa'), (7, 'a', 'mehh'), (9, 'b', 'nope')]
OUT: [('a', 'hoo'), ('a', 'byee'), ('a', 'yoo,laa,mehh'), ('b', 'nope')]
This takes care of both guaranteeing sequential numbers at the 0th-index as well as equal values at the 1st-index.
Edit: I've updated the algorithm according to the requirements. You can group all tuples with the same key by calling group(values, sort=True) or only group adjacent tuples with the same key by calling group(values). This algorithm also gathers all elements after the key for the final tuple instead of only grabbing the third element.
GroupBy does this pretty well. You can group the values by the second element in the tuple. Then for each group, grab all of the third elements in the group and join them into one string:
import itertools
def keySelector(tup):
return tup[1]
def group(values, sort=False):
"""
Group tuples by their second element and return a list of
tuples (a, b) where a is the second element and b is the
aggregated string containing all of the remaining contents
of the tuple.
If sort=True, sort the tuples before grouping. This will
group all tuples with the same key. Otherwise, only adjacent
tuples wth the same key will be grouped.
"""
if sort:
values.sort(key=keySelector)
grouped = itertools.groupby(values, key=keySelector)
result = []
for k, group in grouped:
# For each element in the group, grab the remaining contents of the tuple
allContents = []
for tup in group:
# Convert tuple to list, grab everything after the second item
contents = list(tup)[2:]
allContents.extend(contents)
# Concatenate everything into one string
aggregatedString = ','.join(allContents)
# Add to results
result.append((k, aggregatedString))
return result
vals = [(1,'a','hii','abc','def'),
(2,'a','byee'),
(3,'a','yoo'),
(4,'b','laa'),
(5,'a','mehh','ghi','jkl')]
print(group(vals, sort=True))
Output:
[('a', 'hii,abc,def,byee,yoo,mehh,ghi,jkl'), ('b', 'laa')]
Shortened version with list comprehensions:
def getGroupContents(tuples):
return ','.join(item for tup in tuples for item in list(tup)[2:])
def group(values, sort=False):
if sort:
values.sort(key=keySelector)
grouped = itertools.groupby(values, key=keySelector)
return [(k, getGroupContents(tuples)) for k, tuples in grouped]
Related
I want to find only the top 3 distinct items in descending order. If there's a tiebreaker, sort by alphabetical order. If there are 3 items or fewer, returning the distinct list of items is sufficient.
So if I have input of: ["a","a","b","b","c","c","c","d","d","d","d"]
The output will be ["d","c","a"]
Because d has 4 counts, c 3 counts, a and b have the same frequency, but a is alphabetically first.
In MySQL, I would usually use this:
SELECT id, COUNT(*) as frequency FROM mylist GROUP BY id ORDER BY frequency, id
How can I do that in Python?
I use this code based on SAI SANTOH CHIRAG's solution:
def main(output):
arr = sorted(output,key=lambda i:[output.count(i),-ord(i)],reverse=True)
out = []
for i in arr:
if i not in out: out.append(i)
print(out[:3])
but why is the result like this:
Input (stdin) = a a a b b c d d d d
output = ['d']
['d']
['d']
['d']
['d', 'a']
['d', 'a']
['d', 'a']
['d', 'a', 'b']
['d', 'a', 'b']
['d', 'a', 'b']
instead of what I want, which would be:
['d','a','b']
You use sorted and key for that. Try in this way:
arr = sorted(x,key=lambda i:[x.count(i),-ord(i)],reverse=True)
With this you get all the elements in the sorted order in the increase of count and then alphabetical order. Then do this to get all elements only once:
out = []
for i in arr:
if i not in out:
out.append(i)
print(out[:3])
collections.Counter will do:
the_list = ["a","a","b","b","c","c","c","d","d","d","d"]
counter = Counter(sorted(the_list))
top_3 = counter.most_common(3)
at this point, top_3 is of the form [(<entry>, <freq>)] e.g.
[('d', 4), ('c', 3), ('a', 2)]
Take out the first elements from it via list comprehension:
result = [item for item, freq in top_3]
and we get
['d', 'c', 'a']
Notes:
We pass the sorted list to the Counter because otherwise it will break the ties according to the insertion order; sorting forces the insertion order to be the alphabetical order in a way.
.most_common(3) will return at most 3 elements so we are fine e.g. even if only 2 unique entries are there. E.g. if the_list = ["b", "a"], result will be ["a", "b"] even though number of unique elements is less than 3.
you can use Counter from collections
from collections import Counter
inputs = ["a","a","b","b","c","c","c","d","d","d","d"]
counts = Counter(x)
counts.most_common(3)
final = [i[0] for i in counts.most_common]
output for counts.most_common()
[('d', 4), ('c', 3), ('a', 2), ('b', 2)]
Simpler and more efficient than the accepted answer.
>>> a = ["a","a","b","b","c","c","c","d","d","d","d"]
>>> sorted(set(a), key=lambda s: (-a.count(s), s))[:3]
['d', 'c', 'a']
This removes duplicates first and thus counts each string only once. Also, much better to simply negate the count instead of the character code and using reverse sorting. If the strings had multiple characters, you couldn't even use the character code at all.
Another, using two simpler sorts (might actually be faster):
>>> sorted(sorted(set(a)), key=a.count, reverse=True)[:3]
['d', 'c', 'a']
I am fairly new to python and therefore, although I have been trying to find a solution to this problem since hours, I cant! I have a list of tuples called list_of_tuples and then another list of tuples which is called finalTuple
and to which I have appended two tuples. What I wanna do is read all of the tuples from the list_of_tuples and figure out if there is already an identical tuple in the list.
If there is one I wanna print a message in the console which indicates that otherwise just append the tuple in the finalTuple. Could someone help me with that? I have tried the following code but it doesnt work:
list_of_tuples = [ ("a","b","c"),
("a","b","c"),
("a","b","d"),
("a","b","d"),
("i","k","l")
]
first_tuple = ("a","b","c")
second_tuple= ("a","b","d")
finalTuple = []
finalTuple.append(first_tuple)
finalTuple.append(second_tuple)
for i in range(len(list_of_tuples)):
# print(listtt[i])
if not(any((list_of_tuples[i]) in j for j in finalTuple)) :
key_value = []
key_value.append(list_of_tuples[i])
finalTuple.append(tuple(key_value))
print("The tuple is appended to the list")
if (any((list_of_tuples[i]) in j for j in finalTuple)) :
print("The value already exists")
The output I am getting on the console is:
PS C:\Users\andri\PythonProjects\mypyth> py test.py
The tuple is appended to the list
The value already exists
The value already exists
The tuple is appended to the list
The value already exists
The value already exists
The tuple is appended to the list
The value already exists
Your if block that checks if the value already exists takes place after the if block that checks if it doesn't, append the value to the list, so the former is always True since the value would be appended to the list even if it did not. You should use an else block for the opposite condition instead. Moreover, to check if a tuple already exists in a list of tuples, you can simply use the in operator instead:
for i in range(len(list_of_tuples)):
if list_of_tuples[i] in finalTuple:
print("The value already exists")
else:
finalTuple.append(list_of_tuples[i])
print("The tuple is appended to the list")
lot = [("a","b","c"),
("a","b","c"),
("a","b","d"),
("a","b","d"),
("i","k","l")]
ft = [("a","b","c"),("a","b","d")]
Use in or not in for membership testing .
>>> for thing in lot:
... if thing in ft:
... print(f'{thing} in ft')
... else:
... ft.append(thing)
('a', 'b', 'c') in ft
('a', 'b', 'c') in ft
('a', 'b', 'd') in ft
('a', 'b', 'd') in ft
>>> ft
[('a', 'b', 'c'), ('a', 'b', 'd'), ('i', 'k', 'l')]
>>>
Or use sets for membership testing.
>>> set(lot).difference(ft)
{('i', 'k', 'l')}
>>> ft.extend(set(lot).difference(ft))
>>> ft
[('a', 'b', 'c'), ('a', 'b', 'd'), ('i', 'k', 'l')]
>>>
I know how to match tuples in list. It's
a=[('a','b','c'),('d','e','f'),('g','h','i')]
if ('a','b','c') in a:
print('it is')
But I need a partial match. ('a', 'b', 'j') should match ('a', 'b', 'c').
Maybe b[:2] for b in a?
If you are looking to match the content which are in any specific sub-list independent of the order of occurence, you may use all() and any() with list comprehension expression as:
>>> a=[('a','b','c'),('d','e','f'),('g','h','i')]
>>> match = ('a', 'c') # Not together at `a[0]`
>>> any(all(m in b for m in match) for b in a)
True # since ('a', 'c') are present at `a[0]`
If the order of the match list is to be considered, you may update the above code using tuple slicing as:
>>> match = ('b', 'c') # together at `a[0]`
>>> any(match == b[i:i+len(match)] for b in a for i in range(len(b[:-len(match)+1])))
True # since ('b', 'c') are present together at `a[0]`
a=[('a','b','c'),('d','e','f'),('g','h','i'), ('a', 'b', 'j')]
to_match = ('a', 'b')
for item in a:
if all(j in item for j in to_match): # to check all the to_match element in the a list's element.
print(item)
out:
('a', 'b', 'c')
('a', 'b', 'j')
It turned out I need a little bit different thing.
[sc for sc in a if sc[:2] == ("a", "b")]
did it for me.
Currently I have a file with 6 rows of numbers and each row containing 9 numbers. The point is to test each row of numbers in the file if it completes a magic square. So for example, say a row of numbers from the file is 4 3 8 9 5 1 2 7 6. The first three numbers need to be the first row in a matrix. The next three numbers need to be the second row, and same for the third.
Therefore you would need to end up with a matrix of:
[['4','3','8'],['9','5','1'],['2','7','6']]
I need to test the matrix to see if it is a valid magic square (Rows add up to 15, columns add to 15, and diagonals add to 15).
My code is currently:
def readfile(fname):
"""Return a list of lines from the file"""
f = open(fname, 'r')
lines = f.read()
lines = lines.split()
f.close()
return lines
def assignValues(lines):
magicSquare = []
rows = 3
columns = 3
for row in range(rows):
magicSquare.append([0] * columns)
for row in range(len(magicSquare)):
for column in range(len(magicSquare[row])):
magicSquare[row][column] = lines[column]
return magicSquare
def main():
lines = readfile(input_fname)
matrix = assignValues(lines)
print(matrix)
Whenever I run my code to test it, I'm getting:
[['4', '3', '8'], ['4', '3', '8'], ['4', '3', '8']]
So as you can see I am only getting the first 3 numbers into my matrix.
Finally, my question is how would I go by continuing my matrix with the following 6 numbers of the line of numbers? I'm not sure if it is something I can do in my loop, or if I am splitting my lines wrong, or am I completely on the wrong track?
Thanks.
To test if each row in your input file contains magic square data you need to re-organize the code slightly. I've used a different technique to Francis to fill the matrix. It might be a bit harder to understand how zip(*[iter(seq)] * size) works, but it's a very useful pattern. Please let me know if you need an explanation for it.
My code uses a list of tuples for the matrix, rather than a list of lists, but tuples are more suitable here anyway, since the data in the matrix doesn't need to be modified. Also, I convert the input data from str into int, since you need to do arithmetic on the numbers to test if matrix is a magic square.
#! /usr/bin/env python
def make_square(seq, size):
return zip(*[iter(seq)] * size)
def main():
fname = 'mydata'
size = 3
with open(fname, 'r') as f:
for line in f:
nums = [int(s) for s in line.split()]
matrix = make_square(nums, size)
print matrix
#Now call the function to test if the data in matrix
#really is a magic square.
#test_square(matrix)
if __name__ == '__main__':
main()
Here's a modified version of make_square() that returns a list of lists instead of a list of tuples, but please bear in mind that a list of tuples is actually better than a list of lists if you don't need the mutability that lists give you.
def make_square(seq, size):
square = zip(*[iter(seq)] * size)
return [list(t) for t in square]
I suppose I should mention that there's actually only one possible 3 x 3 magic square that uses all the numbers from 1 to 9, not counting rotations and reflections. But I guess there's no harm in doing a brute-force demonstration of that fact. :)
Also, I have Python code that I wrote years ago (when I was first learning Python) which generates magic squares of size n x n for odd n >= 5. Let me know if you'd like to see it.
zip and iterator objects
Here's some code that briefly illustrates what the zip() and iter() functions do.
''' Fun with zip '''
numbers = [1, 2, 3, 4, 5, 6]
letters = ['a', 'b', 'c', 'd', 'e', 'f']
#Using zip to create a list of tuples containing pairs of elements of numbers & letters
print zip(numbers, letters)
#zip works on other iterable objects, including strings
print zip(range(1, 7), 'abcdef')
#zip can handle more than 2 iterables
print zip('abc', 'def', 'ghi', 'jkl')
#zip can be used in a for loop to process two (or more) iterables simultaneously
for n, l in zip(numbers, letters):
print n, l
#Using zip in a list comprehension to make a list of lists
print [[l, n] for n, l in zip(numbers, letters)]
#zip stops if one of the iterables runs out of elements
print [[n, l] for n, l in zip((1, 2), letters)]
print [(n, l) for n, l in zip((3, 4), letters)]
#Turning an iterable into an iterator object using the iter function
iletters = iter(letters)
#When we take some elements from an iterator object it remembers where it's up to
#so when we take more elements from it, it continues from where it left off.
print [[n, l] for n, l in zip((1, 2, 3), iletters)]
print [(n, l) for n, l in zip((4, 5), iletters)]
#This list will just contain a single tuple because there's only 1 element left in iletters
print [(n, l) for n, l in zip((6, 7), iletters)]
#Rebuild the iletters iterator object
iletters = iter('abcdefghijkl')
#See what happens when we zip multiple copies of the same iterator object.
print zip(iletters, iletters, iletters)
#It can be convenient to put multiple copies of an iterator object into a list
iletters = iter('abcdefghijkl')
gang = [iletters] * 3
#The gang consists of 3 references to the same iterator object
print gang
#We can pass each iterator in the gang to zip as a separate argument
#by using the "splat" syntax
print zip(*gang)
#A more compact way of doing the same thing:
print zip(* [iter('abcdefghijkl')]*3)
Here's the same code running in the interactive interpreter so you can easily see the output of each statement.
>>> numbers = [1, 2, 3, 4, 5, 6]
>>> letters = ['a', 'b', 'c', 'd', 'e', 'f']
>>>
>>> #Using zip to create a list of tuples containing pairs of elements of numbers & letters
... print zip(numbers, letters)
[(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd'), (5, 'e'), (6, 'f')]
>>>
>>> #zip works on other iterable objects, including strings
... print zip(range(1, 7), 'abcdef')
[(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd'), (5, 'e'), (6, 'f')]
>>>
>>> #zip can handle more than 2 iterables
... print zip('abc', 'def', 'ghi', 'jkl')
[('a', 'd', 'g', 'j'), ('b', 'e', 'h', 'k'), ('c', 'f', 'i', 'l')]
>>>
>>> #zip can be used in a for loop to process two (or more) iterables simultaneously
... for n, l in zip(numbers, letters):
... print n, l
...
1 a
2 b
3 c
4 d
5 e
6 f
>>> #Using zip in a list comprehension to make a list of lists
... print [[l, n] for n, l in zip(numbers, letters)]
[['a', 1], ['b', 2], ['c', 3], ['d', 4], ['e', 5], ['f', 6]]
>>>
>>> #zip stops if one of the iterables runs out of elements
... print [[n, l] for n, l in zip((1, 2), letters)]
[[1, 'a'], [2, 'b']]
>>> print [(n, l) for n, l in zip((3, 4), letters)]
[(3, 'a'), (4, 'b')]
>>>
>>> #Turning an iterable into an iterator object using using the iter function
... iletters = iter(letters)
>>>
>>> #When we take some elements from an iterator object it remembers where it's up to
... #so when we take more elements from it, it continues from where it left off.
... print [[n, l] for n, l in zip((1, 2, 3), iletters)]
[[1, 'a'], [2, 'b'], [3, 'c']]
>>> print [(n, l) for n, l in zip((4, 5), iletters)]
[(4, 'd'), (5, 'e')]
>>>
>>> #This list will just contain a single tuple because there's only 1 element left in iletters
... print [(n, l) for n, l in zip((6, 7), iletters)]
[(6, 'f')]
>>>
>>> #Rebuild the iletters iterator object
... iletters = iter('abcdefghijkl')
>>>
>>> #See what happens when we zip multiple copies of the same iterator object.
... print zip(iletters, iletters, iletters)
[('a', 'b', 'c'), ('d', 'e', 'f'), ('g', 'h', 'i'), ('j', 'k', 'l')]
>>>
>>> #It can be convenient to put multiple copies of an iterator object into a list
... iletters = iter('abcdefghijkl')
>>> gang = [iletters] * 3
>>>
>>> #The gang consists of 3 references to the same iterator object
... print gang
[<iterator object at 0xb737eb8c>, <iterator object at 0xb737eb8c>, <iterator object at 0xb737eb8c>]
>>>
>>> #We can pass each iterator in the gang to zip as a separate argument
... #by using the "splat" syntax
... print zip(*gang)
[('a', 'b', 'c'), ('d', 'e', 'f'), ('g', 'h', 'i'), ('j', 'k', 'l')]
>>>
>>> #A more compact way of doing the same thing:
... print zip(* [iter('abcdefghijkl')]*3)
[('a', 'b', 'c'), ('d', 'e', 'f'), ('g', 'h', 'i'), ('j', 'k', 'l')]
>>>
it only gets the first 3 column always because
magicSquare[row][column] = lines[column]
thus
def assignValues(lines):
magicSquare = []
rows = 3
columns = 3
for row in range(rows):
magicSquare.append([0] * columns)
for line in range((sizeof(lines)/9)) #since the input is already split this means that the size of 'lines' divided by 9 is equal to the number of rows of numbers
for row in range(len(magicSquare)):
for column in range(len(magicSquare[row])):
magicSquare[row][column] = lines[(9*line)+(3*row)+column]
return magicSquare
note that (3*row)+column will move to it 3 columns to the right every iteration
and that (9*line)+(3*row)+column will move to it 9 columns (a whole row) to the right every iteration
once you get this you are now ready to process in finding out for the magic square
def testMagicSquare(matrix):
rows = 3
columns = 3
for a in len(matrix)
test1 = 0
test2 = 0
test3 = 0
for b in range(3)
if(sum(matrix[a][b])==15) test1=1 #flag true if whole row is 15 but turns false if a row is not 15
else test1=0
if((matrix[a][0][b]+matrix[a][1][b]+matrix[a][2][b])==15) test2=1 #flag true if column is 15 but turns false if a column is not 15
else test2=0
if(((matrix[a][0][0]+matrix[a][1][1]+matrix[a][2][2])==15) and
((matrix[a][0][2]+matrix[a][1][1]+matrix[a][2][0])==15)) test3=1 #flag true if diagonal is 15 but turns false if diagonal is not 15
else test3=0
if(test1>0 and test2>0 and test3>0) println('line ' + a + ' is a magic square')
else println('line ' + a + ' is not a magic square')
I was wondering if there are any commands to automatically select the next item in the tuple without me having to type it out?
eg.
nul = 0
noofvalue = 5
value = ['a', 'b', 'c', 'd', 'e']
for nul < noofvalue:
file.write(value[0])
what command can i use here to add 1 to 'value' such that when the file loops, instead of using value[0], it uses value[1] instead?
nul = nul + 1
I've googled for the answer and searched, but i don't understand what they are talking about since i'm extremely new to computer coding, so please forgive my ignorance.
I think what you want is enumerate(). I'll add my own example, since your example is a bit weird:
>>> L = ['a', 'b', 'c', 'd', 'e']
>>> for index, value in enumerate(L):
... try:
... print L[index+1] # Prints the next item in the list
... except IndexError:
... print 'End of the list!'
...
b
c
d
e
End of the list!
In Python, you can iterate over a list or tuple in the same way:
for x in value:
do_something(x)
First value = ['a', 'b', 'c', 'd', 'e'] is not tuple, it is a list. In Python to iterate in for loop you can simply do like:
for v in value:
print v # file.write(v)
(I think you have C background where we need index to access elements and iterate over arrays).
if you wants index also then use use `enumerate( any_sequence) function that return (index, value) pairs in list,
>>> list(enumerate(value))
[(0, 'a'), (1, 'b'), (2, 'c'), (3, 'd'), (4, 'e')]
so you could do like:
for i, v in enumerate(value):
print i, v
of course if you want to use index explicitly do like:
index = 0
for v in value:
print index, v
index += 1
but this is not Pythonic way so not preferable in genral.