Python: Act upon (sub)list depending on elements - python

[I'm starting out with python; sorry if the following is dumb, but I've wrapped my head around this all day long and feel that I won't be able to solve this myself.]
I've got a list like:
list = [['a', 'A', '10.0.0.2'], ['a', 'TXT', '1'], ['a', 'TXT', '2'], ['b', 'A', '10.10.10.10'], ['c', 'A', '10.0.0.3'], ['c', 'TXT', '3'], ['c', 'TXT', '4']]
This example shows the list with seven sublists, but there could be n.
What I would like to achieve: Each unique list[i][0] (in this case, 'a', 'b', 'c') should have 'A' and 'TXT' in the corresponding sublists, so in this example, keep all list[i][0] == 'a' and list[i][0] == 'c', delete all occurrences of list[i][0] == 'b'.
I've tried various stuff to accomplish this, but nothing to show actually, because I'm missing an idea, how to do this.
What I've did:
Get unique list[i][0]:
names = [list[i][0] for i in range(len(list))]
names_unique = list(set(names))
But then...how to proceed? I guess, something like for ... in ... should do the trick? Could anybody shed some light on this? Would be greatly appreciated!
Edit 1: Sorry for not being clear: 'a', 'b' and 'c' are just arbitrary values. I don't know these in advance, it's output of a dns zone transfer. I would like to keep all hostnames (here: 'a', 'b', 'c') which got associated A and TXT records, and drop these without TXT. I can't hardcode any names like 'a', b' or 'c', because these change; instead, I'm trying to come up with code which does what I've described here.
Desired output:
list = [['a', 'A', '10.0.0.2'], ['a', 'TXT', '1'], ['a', 'TXT', '2'], ['c', 'A', '10.0.0.3'], ['c', 'TXT', '3'], ['c', 'TXT', '4']]

You could do this:
'define a function that checks if a key is valid or not'
def isValid(key, l):
return all(_ in (x[1] for x in filter(lambda x:x[0] == key,l)) for _ in ['TXT','A'])
keys = list(set(x[0] for x in l))
keysValid = []
for key in keys:
if isValid(key, l): keysValid.append(key)
filtered_list = list(filter(lambda x: x[0] in keysValid, l))
all this does is get all possible keys, then add all valid keys into a new list. It then uses filter on the original list to check if the key is in the valid key list.
This is an ugly one liner that you could also use:
>>> l = [['a', 'A', '10.0.0.2'], ['a', 'TXT', '1'], ['a', 'TXT', '2'], ['b', 'A', '10.10.10.10'], ['c', 'A', '10.0.0.3'], ['c', 'TXT', '3'], ['c', 'TXT', '4']]
>>> #this monstrosity right here
>>> u = list(filter(lambda x: x[0] in filter(lambda key: all(_ in (x[1] for x in filter(lambda x:x[0] == key,l)) for _ in ['TXT','A']),set(li[0] for li in l)),l))
>>> u
[['a', 'A', '10.0.0.2'], ['a', 'TXT', '1'], ['a', 'TXT', '2'], ['c', 'A', '10.0.0.3'], ['c', 'TXT', '3'], ['c', 'TXT', '4']]
MAJOR NOTE: this is sort of unrelated to your original question but do not use list as a variable name. This will disallow any call to python's cast to list function list() which is useful when you are using filter()/set() because they return filter/set objects rather than list.

You could try using filter and a lambda expression. For example:
acceptable_elements = ['a','b']
filtered_list = filter(lambda sublist: (sublist[0] in acceptable_elements), my_list)
This will check all elements of the list, and won't alter your original list. I can't precisely tell from your example whether or not you want to check just in the first position or in the entire list, if you want to check for the existence of elements within the entire list:
acceptable_elements = ['a','b']
filtered_list = filter(lambda sublist: any([c in acceptable_elements for c in sublist]), my_list)

Related

Joining values of a list inside another list into a string

Im trying to join the letters as a string that's inside the list which is also inside the list. So for example, it looks like this [['a', 'b', 'c'], ['d', 'e', 'f']] however I want the result to look like 'ad be cf' which is basically taking the element that lies in the same position in the list. I know how to join the elements into a list that can look like 'abcdef', however, i don't know which I could add in order to return a string that looks like above.
Any advice would be thankful!
string = ''
new_grid = []
for a in grid:
for b in a:
string += b
return string
When you want to transpose lists into columns, you typically reach for zip(). For example:
l = [['a', 'b', 'c'], ['d', 'e', 'f']]
# make a list of columns
substrings = ["".join(sub) for sub in zip(*l)]
#['ad', 'be', 'cf']
print(" ".join(substrings))
# alternatively print(*substrings, sep=" ")
# ad be cf
This works:
my_list = [['a', 'b', 'c'], ['d', 'e', 'f']]
sorted_list = [list(pair) for pair in zip(my_list[0], my_list[1])]
for i in range(3):
string = ''.join(sorted_list[i])
print(string, end=" ")
First, we are pairing each individual list to its corresponding value using [zip][1], then we are joining it into a string, and printing it out.
This solution may not be the most efficient, but it's simple to understand.
Another quick solution without zip could look like this:
my_list = [['a', 'b', 'c'], ['d', 'e', 'f']]
sorted_list = list(map(lambda a, b: a + b, my_list[0], my_list[1]))
print(" ".join(sorted_list))

How to convert numerical strings inside a mixed list of list to int (Python)

how do I convert all the numerical strings, inside a list of list that contains both alphabetical, and numerical strings, into an integer?
My Output:
[['69', ' Test', 'Results'], ['A', 'B', 'C'], ['D', '420', 'F']]
Intended Output:
[[69, ' Test', 'Results'], ['A', 'B', 'C'], ['D', 420, 'F']]
Note that my code reads a CSV file. Thanks everyone
def get_csv_as_table(a, b):
s = False
import csv
with open(a) as csv_file:
file_reader = csv.reader(csv_file, delimiter=b)
member = list(file_reader)
print(member)
print ("Enter filename: ")
a = input()
print ("Enter the delimiter: ")
b = input()
get_csv_as_table(a, b)
You can use list comprehension to achieve this. The only minor downside to this is that you will be creating a new list for this instead of modifying the existing list.
my_list = [['69', 'Test', 'Results'], ['A', 'B', 'C'], ['D', '420', 'F']]
filtered_list = [
[int(item) if item.isdigit() else item for item in sub_list]
for sub_list in my_list
]
If you want to edit the list in-place, you can use traditional for-loop. The following code will edit the existing list without creating a new list. This could turn out to be useful in case you have a large list.
my_list = [['69', 'Test', 'Results'], ['A', 'B', 'C'], ['D', '420', 'F']]
for i in range(len(my_list)):
for j in range(len(my_list[i])):
if my_list[i][j].isdigit():
my_list[i][j] = int(my_list[i][j])
str.isdigit() checks if a given string is a number or not. An important note to keep in mind is that, it does not work for floating-point numbers, just integers. Once the condition passes, the item is converted to integer.
Yoy have to combine 2 levels of list-comprehension and use str.isdigit()
values = [
[int(val) if val.isdigit() else val for val in row]
for row in values
]
Try with 2-level list comprehension and int()+.isdigit() power combo in list comprehension ;-)
l=[['69', ' Test', 'Results'], ['A', 'B', 'C'], ['D', '420', 'F']]
l=[[int(y) if y.isdigit() else y for y in x] for x in l]
print(l)
Output:
[[69, ' Test', 'Results'], ['A', 'B', 'C'], ['D', 420, 'F']]
.isdigit() only works on string representation of pure integers, In case if you have floats too then replace '.' to nothing ;-)
l=[['69', ' Test', 'Results'], ['A', 'B', 'C'], ['D', '420', 'F']]
l=[[float(y) if y.replace('.','').isdigit() else y for y in x] for x in l]
print(l)
Output:
[[69.0, ' Test', 'Results'], ['A', 'B', 'C'], ['D', 420.0, 'F']]

How to split an array of arrays?

I have this array that contains some other arrays in Python, but I need only the first elements of each mini array inside the main array. Is there some method to do that?
Example:
array = [['a','1'], ['b','2'], ['c','3'], ['d','4'], ['e','5']]
I need the letters in one row:
'a'
'b'
'c'
'd'
'e'
And the numbers in another:
'1'
'2'
'3'
'4'
'5'
can You help me with this?
You can use zip to separate letters from numbers and map to convert the tuples returned by zip to lists:
array = [['a','1'], ['b','2'], ['c','3'], ['d','4'], ['e','5']]
letters, numbers = map(list, zip(*array))
print(letters)
print(numbers)
Output:
['a', 'b', 'c', 'd', 'e']
['1', '2', '3', '4', '5']
You can use comprehension. a[0] means first item in a list
[a[0] for a in array]
Result:
['a', 'b', 'c', 'd', 'e']
You can use
letters,numbers = tuple(zip(*array))

Split list into list of lists by regex

I want to split a character list into a list of lists, where the split point is defined by successful Regex match.
For instance, say I have an input list:
["file1","A","B","C","file2","D","E","F","G","H","I"]
I want to produce:
[["file1","A","B","C"],["file2","D","E","F","G","H","I"]]
Where the split points, being file1 and file2 were identified by a successful match to
re.search("file[0-9]+",<TEST STRING>)
It is NOT known in advance, the number of items between each split point, nor is it known how many 'fileXXX' terms are in the original vector.
In reality, my Regex matches are a lot more complicated than this, that is not the concern, what I need help with, if someone would be so kind, is the Pythonic way to execute the split logic?
Assumes the first element will be a proper header. If not, you will need to do some defensive clauses.
import re
result = []
pattern = re.compile(r'^file.*')
for el in input_list:
if pattern.match(el):
row = []
result.append(row)
row.append(el)
The following should work quite nicely:
import re
input_list = ["file1","A","B","C","file2","D","E","F","G","H","I"]
output_list = []
for item in input_list:
if re.match("file[0-9]+", item):
output_list.append([item])
else:
output_list[-1].append(item)
print output_list
Gives the following result:
[['file1', 'A', 'B', 'C'], ['file2', 'D', 'E', 'F', 'G', 'H', 'I']]
Note, this assumes the first item is a match.
Update
A second approach could be:
input_list = ["1", "2", "file1","A","B","C","file2","D","E","F","G","H","I"]
output_list = []
for item in input_list:
if re.match("file[0-9]+", item) or len(output_list) == 0:
output_list.append([item])
else:
output_list[-1].append(item)
print output_list
This would also cope with the non initial match case:
[['1', '2'], ['file1', 'A', 'B', 'C'], ['file2', 'D', 'E', 'F', 'G', 'H', 'I']]
You can find the indexes of file\d:
indeces = list(i for i,val in enumerate(my_list) if match('file\d', val))
And then simply group by these indexes:
output = [my_list[indeces[0]:indeces[1]], my_list[indeces[1]:]]
>>> from re import match
>>> my_list = ["file1","A","B","C","file2","D","E","F","G","H","I"]
>>> indeces = list(i for i,val in enumerate(my_list) if match('file\d', val))
>>> [my_list[indeces[0]:indeces[1]], my_list[indeces[1]:]]
[['file1', 'A', 'B', 'C'], ['file2', 'D', 'E', 'F', 'G', 'H', 'I']]

search an item of sublist in another list of list by position

I have a list of list created like
biglist=[['A'], ['C', 'T'], ['A', 'T']]
and I will have another list like
smalllist=[['C'], ['T'], ['A', 'T']]
So, I want to check wheter an item in small list contains in that specific index of biglist, if not append to it.
so, making
biglist=[['A','C'], ['C', 'T'], ['A', 'T']]
so, 'C' from fist sublist of smalllist was added to first sublist of biglist. but not for second and third.
I tried like
dd=zip(biglist, smalllist)
for each in dd:
ll=each[0].extend(each[1])
templist.append(list(set(ll)))
but get errors
templist.append(list(set(ll)))
TypeError: 'NoneType' object is not iterable
How to do it?
Thank you
Probably, you should try this:
// This will only work, if smalllist is shorter than biglist
SCRIPT:
biglist = [['A'], ['C', 'T'], ['A', 'T']]
smalllist = [['C'], ['T'], ['A', 'T']]
for i, group in enumerate(smalllist):
for item in group:
if item not in biglist[i]:
biglist[i].append(item)
DEMO:
print(biglist)
# [['A', 'C'], ['C', 'T'], ['A', 'T']]
[list(set(s+b)) for (s,b) in zip(smalllist,biglist)]
For some reason, extend in Python doesn't return the list itself after extending. So ll in your case is None. Just put ll=each[0] on the second line in the loop, and your solution should start working.
Still, I'm not getting, why you don' keep your elements in sets in the first place. This would avoid you from having to convert from list to set and then backwards.
I would just or sets instead of appending to the list and then filtering out duplicates by resorting to set and then to list.
>>> from itertools import izip
>>> templist = []
>>> for els1,els2 in izip(biglist,smalllist):
joined = list(set(els1) | set(els2))
templist.append(joined)
>>> templist
[['A', 'C'], ['C', 'T'], ['A', 'T']]
Keeping elements in sets in the first place seems to be the fastest in Python 3 even for such small amount of elements in each set (see comments):
biglist=[set(['A']), set(['C', 'T']), set(['A', 'T'])]
smalllist=[set(['C']), set(['T']), set(['A', 'T'])]
for els1,els2 in zip(biglist,smalllist):
els1.update(els2)
print(biglist)
Ouput:
[{'A', 'C'}, {'C', 'T'}, {'A', 'T'}]

Categories

Resources