user393267
user393267

Reputation:

Python: how to work with list to have unique values

The problem that I have is the following: I get from a process, a set of values:

t=903
a=432
c=335

And these values goes in a dictionary, which assign different sequences of t, a and c to different keys

{key1} : (t1 a1 c1, t2 a2 c2, ...tn an cn)
{key2} : (t1 a1 c1, t2 a2 c2, ...tn an cn)

What I would like, is to not add a triplet of values to a key, if the first value (say t), is already present. Basically I would use "t" variable as unique factor for my triplet. This only apply for each key, so I don't mind if I have the same value on key 2...key n; the important is that each key has an unique value of t, and that the 3 values are written in the dictionary only if the value of "t" is unique.

I tried this:

for triplets in dict[key1]:
    if not t in triplets:
        dict[key1].append(t,a,c)

But this seems to not be working. Am I cycling incorrectly trough the dictionary? Should I use a different structure?

Upvotes: 0

Views: 57

Answers (2)

Martijn Pieters
Martijn Pieters

Reputation: 1124548

You can create another level; each value a dictionary keyed on t:

tripplets = dict.setdefault(key1, {})
if t not in triplets:
    triplets[t] = (t, a, c)

This produces:

{
    'key1': {'t1': (t1, a1, c2),
             't2': (t2, a2, c2)},
    ...
}

so only add (t?, a?, c?) if t? is not already a key in the dict[key1] dictionary.

You could also replace your triple-value tuples with a custom class that implements __eq__ and __hash__ to be considered equal if their t value is equal:

class Entry(object):
    __slots__ = ('t', 'a', 'c')  # save some memory
    def __init__(self, t, a, c):
        self.t = t
        self.a = a
        self.c = c
    def __eq__(self, other):
        if not isinstance(other, Entry): return NotImplemented
        return self.t == other.t
    def __hash__(self):
        return id(self.t)
    def __repr__(self):
        return '<{0}({1[0]!r}, {1[1]!r}, {1[2]!r})>'.format(type(self).__name__, self)
    def __getitem__(self, index):
        return getattr(self, 'tac'[index])

then use sets in your dictionary:

dict(key1, set()).add(Entry(t, a, c))

If however, you sometimes wanted to keep t unique, and a or c at other times, there is nothing for it but to do an actual search. Use the any() function to bail out early if a match has been found:

triplets = dict.setdefault(key1, [])
if not any(triplet[0] == t for triplet in triplets):
    # no such t value found
    triplets.append((t, a, c))

Upvotes: 2

aruisdante
aruisdante

Reputation: 9105

This sounds a whole lot like... a second dictionary! You could implement your data-structure as follows:

from collections import defaultdict

items = defaultdict(dict)

def add_entry(items, key, t, a, c):
    values = items[key]
    if t not in values:
        values[t] = (a,c)

def get_entry(items, key):
    values = items[key]
    entry  = []
    for t, (a,c) in values.iteritems():
        entry.extend((t,a,c))
    return entry

Usage:

>>> add_entry(items, "test", 1, 2, 3)
>>> get_entry(items, "test")
[1, 2, 3]
>>> add_entry(items, "test", 2, 2, 3)
>>> get_entry(items, "test")
[1, 2, 3, 2, 2, 3]
>>> add_entry(items, "test", 1, 6, 7)
>>> get_entry(items, "test")
[1, 2, 3, 2, 2, 3]
>>> add_entry(items, "test2", 1, 2, 3)
>>> get_entry(items, "test2")
[1, 2, 3]

Replace entry.extend((t,a,c)) with entry.append((t,a,c)) if you want to get back a list of tuples rather than just a list of values.

And of course you could encapsulate this datastructure and the methods that operate on it into a class for better usability if you'd like, like so:

class EntryManager(object):

    def __init__(self):
        self._items = defaultdict(dict)

    def add_entry(self, key, t, a, c):
        values = self._items[key]
        if t not in values:
            values[t] = (a,c)

    def get_entry(self, key):
        values = self._items[key]
        entry  = []
        for t, (a,c) in values.iteritems():
            entry.extend((t,a,c))
        return entry

Upvotes: 0

Related Questions