What are the pythonic way to replace a specific set element? - python

I have a python set set([1, 2, 3]) and always want to replace the third element of the set with another value.
It can be done like below:
def change_last_elemnent(data):
result = []
for i,j in enumerate(list(data)):
if i == 2:
j = 'C'
result.append(j)
return set(result)
But is there any other pythonic way to do that,more smartly and making it more readable?
Thanks in advance.

Sets are unordered, so the 'third' element doesn't really mean anything. This will remove an arbitrary element.
If that is what you want to do, you can simply do:
data.pop()
data.add(new_value)
If you wish to remove an item from the set by value and replace it, you can do:
data.remove(value) #data.discard(value) if you don't care if the item exists.
data.add(new_value)
If you want to keep ordered data, use a list and do:
data[index] = new_value
To show that sets are not ordered:
>>> list({"dog", "cat", "elephant"})
['elephant', 'dog', 'cat']
>>> list({1, 2, 3})
[1, 2, 3]
You can see that it is only a coincidence of CPython's implementation that '3' is the third element of a list made from the set {1, 2, 3}.
Your example code is also deeply flawed in other ways. new_list doesn't exist. At no point is the old element removed from the list, and the act of looping through the list is entirely pointless. Obviously, none of that really matters as the whole concept is flawed.

Related

update the value in set Python?

Here an example of my set, which I would like to update:
>>> x = set()
>>> x.add(('A1760ulorenaf0821x151031175821564', 1, 0))
>>> x
set([('A1760ulorenaf0821x151031175821564', 1, 0)])
My expected result would be:
set([('A1760ulorenaf0821x151031175821564', 1, 1)])
How I can do it? Is a set is the best option, or have I to use another data structure for that? I thought that update method in set can do that. However, I made mistake, and that is not a good solution as it does not consider the first parameter as a key and repeat the elements.
You'll have to remove the element from the set, and add a new element with that value updated. That's because sets use hashing to efficiently eliminate duplicates. If mutating the elements directly was allowed you'd break this model.
I think you only want the first element to be unique, and track some data associated with that first element. If so you want to use a dictionary instead; use the first element as a key to map to the other two values, in a list for easy altering:
>>> x = {}
>>> x['A1760ulorenaf0821x151031175821564'] = [1, 0]
>>> x['A1760ulorenaf0821x151031175821564'][1] += 1 # increment the second element.
>>> x
{'A1760ulorenaf0821x151031175821564': [1, 1]}
Keys in a dictionary also must be unique.
Note that set.update() only gives you a union operation; the set is updated in-place by adding all elements from the argument(s) that are not already in the set. set.update() cannot alter elements already in the set, because elements the set should not be altered (or at least not in ways that change their hash and equality).
You are better off using a dict if you are trying to have keys and values and you want to update based on key:
x = {'A1760ulorenaf0821x151031175821564' : [1, 0]}
x['A1760ulorenaf0821x151031175821564'] = [1, 1]

How to find something in a 2D array when only looking for one item of the 2D array?

for example if I had
array=[["A",1],["B",2],["C",1]]
is there any way I can find ["A",1] by just looking for "A"? I'm trying to use this in a situation where the first thing in the array is unique so there's no point in looking at the second, also I have no way of knowing what the second variable is
Iterate over items present inside the outer list and check that the first element of inner list satisfies a particular condition.
>>> a=[["A",1],["B",2],["C",1]]
>>> next(i for i in a if i[0] == 'A')
['A', 1]
>>> [i for i in a if i[0] == 'A']
[['A', 1]]
If you're in control of the datatype, depending on whate else you're doing with this object, a dictionary may be a better choice for this:
Rather than
array=[["A",1],["B",2],["C",1]]
use
d={"A":1, "B":2, "C":1}
Then you can access the element associated with "A" simply with
>> d["A"]
1
If you want to transform your list into a dictionary:
d = dict(array)

How to count number of unique lists within list?

I've tried using Counter and itertools, but since a list is unhasable, they don't work.
My data looks like this: [ [1,2,3], [2,3,4], [1,2,3] ]
I would like to know that the list [1,2,3] appears twice, but I cant figure out how to do this. I was thinking of just converting each list to a tuple, then hashing with that. Is there a better way?
>>> from collections import Counter
>>> li=[ [1,2,3], [2,3,4], [1,2,3] ]
>>> Counter(str(e) for e in li)
Counter({'[1, 2, 3]': 2, '[2, 3, 4]': 1})
The method that you state also works as long as there are not nested mutables in each sublist (such as [ [1,2,3], [2,3,4,[11,12]], [1,2,3] ]:
>>> Counter(tuple(e) for e in li)
Counter({(1, 2, 3): 2, (2, 3, 4): 1})
If you do have other unhasable types nested in the sub lists lists, use the str or repr method since that deals with all sub lists as well. Or recursively convert all to tuples (more work).
ll = [ [1,2,3], [2,3,4], [1,2,3] ]
print(len(set(map(tuple, ll))))
Also, if you wanted to count the occurences of a unique* list:
print(ll.count([1,2,3]))
*value unique, not reference unique)
I think, using the Counter class on tuples like
Counter(tuple(item) for item in li)
Will be optimal in terms of elegance and "pythoniticity": It's probably the shortest solution, it's perfectly clear what you want to achieve and how it's done, and it uses resp. combines standard methods (and thus avoids reinventing the wheel).
The only performance drawback I can see is, that every element has to be converted to a tuple (in order to be hashable), which more or less means that all elements of all sublists have to be copied once. Also the internal hash function on tuples may be suboptimal if you know that list elements will e.g. always be integers.
In order to improve on performance, you would have to
Implement some kind of hash algorithm working directly on lists (more or less reimplementing the hashing of tuples but for lists)
Somehow reimplement the Counter class in order to use this hash algorithm and provide some suitable output (this class would probably use a dictionary using the hash values as key and a combination of the "original" list and the count as value)
At least the first step would need to be done in C/C++ in order to match the speed of the internal hash function. If you know the type of the list elements you could probably even improve the performance.
As for the Counter class I do not know if it's standard implementation is in Python or in C, if the latter is the case you'll probably also have to reimplement it in C in order to achieve the same (or better) performance.
So the question "Is there a better solution" cannot be answered (as always) without knowing your specific requirements.
list = [ [1,2,3], [2,3,4], [1,2,3] ]
repeats = []
unique = 0
for i in list:
count = 0;
if i not in repeats:
for i2 in list:
if i == i2:
count += 1
if count > 1:
repeats.append(i)
elif count == 1:
unique += 1
print "Repeated Items"
for r in repeats:
print r,
print "\nUnique items:", unique
loops through the list to find repeated sequences, while skipping items if they have already been detected as repeats, and adds them into the repeats list, while counting the number of unique lists.

python dictionary comprehension iterator

Hey I have a doubt in the following python code i wrote :
#create a list of elements
#use a dictionary to find out the frequency of each element
list = [1,2,6,3,4,5,1,1,3,2,2,5]
list.sort()
dict = {i: list.count(i) for i in list}
print(dict)
In the dictionary compression method, "for i in list" is the sequence supplied to the method right ? So it takes 1,2,3,4.. as the keys. My question is why doesn't it take 1 three times ? Because i've said "for i in list", doesn't it have to take each and every element in the list as a key ?
(I'm new to python so be easy on me !)
My question is why doesn't it take 1 three times ?
That's because dictionary keys are unique. If there is another entry found for the same key, the previous value for that key will be overwritten.
Well, for your issue, if you are only after counting the frequency of each element in your list, then you can use collections.Counter
And please don't use list as variable name. It's a built-in.
>>> lst = [1,2,6,3,4,5,1,1,3,2,2,5]
>>> from collections import Counter
>>> Counter(lst)
Counter({1: 3, 2: 3, 3: 2, 5: 2, 4: 1, 6: 1})
Yes, your suspicion is correct. 1 will come up 3 times during iteration. However, since dictionaries have unique keys, each time 1 comes up it will replace the previously generated key/value pair with the newly generated key/value pair. This will give the right answer, it's not the most efficient. You could convert the list to a set instead to avoid reprocessing duplicate keys:
dict = {i: list.count(i) for i in set(list)}
However, even this method is horribly inefficient because it does a full pass over the list for each value in the list, i.e. O(n²) total comparisons. You could do this in a single pass over the list, but you wouldn't use a dict comprehension:
xs = [1,2,6,3,4,5,1,1,3,2,2,5]
counts = {}
for x in xs:
counts[x] = counts.get(x, 0) + 1
The result for counts is: {1: 3, 2: 3, 3: 2, 4: 1, 5: 2, 6: 1}
Edit: I didn't realize there was something in the library to do this for you. You should use Rohit Jain's solution with collections.Counter instead.

List: Counting and Deleting

Given a list, for example List=["X","X","O","O",'O','O'], how would I count how many "X"'s there are in a list, and then subtract that many "O"s from the list. The List will always put all X's first and all O's last. I thought I could do something like...
List=["X","X","O","O",'O','O']
ListCount=List.count("X")
del List[-(List2):0]
I thought this would yield ["X","X","O","O"] by deleting the O's from -2:0 but absolutely nothing happened. In python.
As noted in my comment, another data structure could be more useful here, as you don't care about order:
data = {"X": 2, "O": 4}
data["O"] -= data["X"]
If you ever need the actual list, it's easy to create with a quick generator expression:
from itertools import chain, repeat
data_list = list(chain.from_iterable(repeat(key, count) for key, count in data.items()))
Or, to go the other way:
from collections import Counter
data = Counter(data_list)
The index zero corresponds to the first item in a list, so your slice List[-2:0] says to go from the next to last to just before the first. Since that's going to be an empty range, you don't get anything from your slice (or rather, your del statement doesn't delete anything).
Instead, leave out the second part of the slice. Python will slice to the end of the list automatically.
del List[-ListCount:]
Assuming I understand your question, your slicing should look like this:
del List[-ListCount:]
Your way seems to work - you shouldn't use List2 bu ListCount in your example. Maybe it's not too pythonic, and it will work only within conditions that you stated but it works :)
>>> l = [1,1,1,2,2,2,2]
>>> del l[-l.count(1):]
>>> print l
[1, 1, 1, 2]
How about:
List = ["X","X","O","O",'O','O']
xCount = List.count('X')
List = List[:-xCount]

Categories

Resources