Some problem with dict function - python

I'm trying to convert a list to a dictionary by using the dict function.
inpu = input.split(",")
dic = dict(inpu)
The above code is trying to get a string and split it on ',' and afterwards I use the dict function to convert the list to a dictionary.
However, I get this error:
ValueError: dictionary update sequence element #0 has length 6; 2 is required
Can anybody help?

dict expects an iterable of 2-element containers (like a list of tuples). You can't just pass a list of items, it doesn't know what's a key and what's a value.
You are trying to do this:
>>> range(10)
<<< [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> dict(range(10))
---------------------------------------------------------------------------
TypeError: cannot convert dictionary update sequence element #0 to a sequence
dict expects a list like this:
>>> zip(lowercase[:5], range(5))
<<<
[('a', 0),
('b', 1),
('c', 2),
('d', 3),
('e', 4)]
The first element in the tuple becomes the key, the second becomes the value.
>>> dict(zip(lowercase[:5], range(5)))
<<<
{'a': 0,
'b': 1,
'c': 2,
'd': 3,
'e': 4}

As listed on the Python Data structures Docs. The dict() constructor builds dictionaries directly from lists of key-value pairs stored as tuples.
so the inpu array must be of form ('key', 'value') at each position, for example
>>> dict([('sape', 4139), ('guido', 4127), ('jack', 4098)])
{'sape': 4139, 'jack': 4098, 'guido': 4127}
your input array is probably greater than 2 in size

A dictionary is a key-value pair which is why it says length-2 lists are needed to match keys to values. You are splitting the input into a flat list, which is why Python is complaining in this case - it doesn't know what to specify as keys and what to specify as values.

Related

searching for a list in a list of ordered dictionaries

So I have a list of ordered dictionaries which all have 'name' keys which take a string as a value and then a series of other keys which take integers as values. I also have a list of integers separate from the list of ordered dictionaries. I would like to search through the list of ordered dictionaries and see if there are any dictionaries which have all the integers in the list, and if so, what the 'name' value in that list is. Is there any way to do this?
I.e. i have a list of dictionaries with dictionaries like so:
dict = OrderedDict({('name' : 'John'), ('int1': 5), ('int2': 3), ('int3': 1)}), OrderedDict({('name': 'Jack'), ('int1': 1), ('int2': 6), ('int3': 7)})
and then a list of integers like: list = [3, 2, 5]
and if there is a match between the list and the integers in any of the ordered dictionaries, I would like to get the name returned (so in the above case, John).
This may be very basic in which case I apologise, I'm very new to python and coding in general. I've been searching for hours but I haven't found anything I can understand.
If I understand your question right (not that the example data, or the result for John is correct), you may be looking for
dicts = [
{"name": "John", "int1": 5, "int2": 3, "int3": 1},
{"name": "Jack", "int1": 1, "int2": 6, "int3": 7},
{"name": "Mallory", "int1": 1, "int2": 6, "int3": 3},
]
def find_with_keyset(dicts, keyset):
for dict in dicts:
if all(
key in dict and dict[key] == value
for (key, value) in keyset.items()
):
yield dict
def find_first_with_keyset(dicts, keyset):
for result in find_with_keyset(dicts, keyset):
return result
for match in find_with_keyset(dicts, {"int2": 6}):
print(match["name"])
print("---")
print(find_first_with_keyset(dicts, {"int1": 5, "int2": 3, "int3": 1}))
This prints out
Jack
Mallory
---
{'name': 'John', 'int1': 5, 'int2': 3, 'int3': 1}
The idea is that the find_with_keyset generator function filters a given iterable of dicts based on a key subset; for ease-of-use there's find_first_with_keyset which will return the first match, or None.
To turn [1, 2, 3] to {'int1': 1, ...} you can use e.g. {f'key{i}': value for (i, value) in enumerate([1, 2, 3], 1)}.

How do I create a class with the specified characteristics?

What I want to do is to create a class that I will call a Cluster that I can use to store representations of multivariable data points. Like a bunch of people's heights, weights, and ages. I want to make it so when I create an instance of Cluster, it returns an empty dictionary. I then want to be able to add key value pairs. I want each value paired with each key to only be a list. I want each list to only contain only 1 data type.
However, they don't have to be the same data type for every list. What I mean is that one list can only consists of integers, and the other list is only full of strings. I even allow a list of tuples or lists or dictionaries. They don't even have to have the same data type for each value in itself. But, the overall list must then contain all dictionaries, tuples, etc.
Example of a Cluster:
{'Hobbies': [(3, 's', {'name': 'Sam'}), (4, 5), (), (True)], 'Weights': [1, 34, 3, 90]}
Notice how the above 'Hobbies' value pair is a list of only tuples and the 'Weights' value pair is a list of only integers. This is what I mean by what I am trying to say above.
I created a function that generates what a Cluster full of values should look like, but I want it to be a class. It gives you what I want to happen, but I want make a class, not a function that generates it. Other methods such as getting the length of the Cluster I can figure out, I just want everything that I said above to be true.
from random import randint
def create_cluster(a, b, c, *d):
cluster = {}
for key in d:
point = []
for integer in range(0, a):
point.append(randint(b, c))
cluster[key] = point
return cluster
print(create_cluster(4, 2, 9, 'Height', 'Weight', 'Age'))
Output:
{'Height': [5, 3, 3, 6], 'Weight': [7, 3, 5, 7], 'Age': [9, 5, 3, 6]}
As Code-Apprentice mentions, there is no mechanism by which python enforces type checking in the sense that it will stop you from compiling and running the code. As of version 3.5, however, Python allows you to implement a soft type checking - you can read about it here. What this means is that Python now offers you a way too check to see if your code is passing illegal values to functions/classes/variables etc.
Here's how it works:
from typing import Dict, List, Any, Tuple, Union
from random import randint
class Cluster:
def __init__(self):
self.data: Dict[str, List[Any]] = {}
def create_cluster(self, a, b, c, *d):
for key in d:
point = []
for integer in range(0, a):
point.append(randint(b, c))
self.data[key] = point
clust = Cluster()
clust.create_cluster(4, 2, 9, 'Height', 'Weight', 'Age')
print(clust.data)
Edit: for your example specifically, you'd want something like:
self.data: Dict[str, List[Union[Tuple, int]] = {}
Union is a type that indicates a wildcard of specified possibilities, so the dictionary value can be a list containing either tuples or ints. I guess if you wanted to further restrict the data types inside the tuples, you could do:
self.data: Dict[str, List[Union[Tuple[Union[int, str]], int]] = {}
Next, you need mypy installed. Mypy has to be called separately from a terminal on your script, but it will go through the code and perform a type check - that is, it will alert you to any instances where you have declared illegal variables in your class.
Alternatively, you could perform a series of in-code catches involving:
if type(data['Weight'][0]) != whatever:
raise("You're putting the wrong type of variable in")
But mypy seems like a better solution in the longer term. It'll save you from having to wait for your code to execute, run, and fail in a predictable spot.
Feedback and corrections welcome.
If I'm understanding what you need, we can actually make a dict object that will type check for you!
No imports needed.
We just have to subclass the dict class, and override the functions for adding items to the dictionary, so we can type check before allowing any additions.
Here is the code below.
class ClusterDict(dict):
key_type = str # we want all of our keys to be str type
value_type = list # same for our value as a list
# lets override the function for dictionary assignment so we can type check before we add to our dictionary
def __setitem__(self, key, value):
self.type_check_and_add(key, value)
# Lets override update as well
def update(self, **kwargs) -> None:
for key, value in kwargs.items():
self.type_check_and_add(key, value)
def type_check_and_add(self, key: str, value: list) -> None:
assert type(key) == self.key_type, f"Expected {self.key_type} for key, got {type(key)} instead."
if type(value) is not list:
value = [value] # Make it a list if its isn't, remove this if you know it will always come as a list, even if its a single entry, like [5], or [(2, 6)]
assert type(value) == self.value_type, f"Expected {self.value_type} for key, got {type(value)} instead."
val_type = type(value[0]) # type of first item in list, lets make sure they all match this one
for val in value:
assert type(val) == val_type, f"Items do not have matching types for key {key}."
super().__setitem__(key, value) # this then passes assignment as usual to the base class dict
Lets do some tests now!
if __name__ == '__main__':
clust = ClusterDict()
input_data = {
'Height': [4, 3, 5],
'Age': [(4, 9), (4, 6, 8)]
}
clust.update(**input_data)
print(clust)
{'Height': [4, 3, 5], 'Age': [(4, 9), (4, 6, 8)]}
Looks good so far, we can add whole dictionaries using update as usual
input_data = {'Weights': [1, 34, 3, 90]}
clust.update(**input_data)
print(clust)
{'Height': [4, 3, 5], 'Age': [(4, 9), (4, 6, 8)], 'Weights': [1, 34, 3, 90]}
Or just one entry at a time.
Lets try the usual way, dict[key] = value
clust['Hobbies'] = [(3, 's', {'name': 'Sam'}), (4, 5), (), (True,)]
print(clust)
{'Height': [4, 3, 5], 'Age': [(4, 9), (4, 6, 8)], 'Weights': [1, 34, 3, 90], 'Hobbies': [(3, 's', {'name': 'Sam'}), (4, 5), (), (True,)]}
Looking good. But now lets try adding a list with types that don't match.
clust['My Favorite Number'] = [7, '7', (1, 1, 1, 1, 1, 1, 1)]
print(clust)
assert type(val) == val_type, f"Items do not have matching types for key {key}."
AssertionError: Items do not have matching types for key My Favorite Number.
Which throws an AssertionError and prevents us from doing so, like we wanted.
Bonus though, because we subclassed dict, we get all of its methods for free!
>>>len(clust)
4
for k, v in clust.items():
... print(k, v)
...
Height [4, 3, 5]
Age [(4, 9), (4, 6, 8)]
Weights [1, 34, 3, 90]
Hobbies [(3, 's', {'name': 'Sam'}), (4, 5), (), (True,)]
Hopefully this is what you were looking for and it helps!

Different ways to access tuples created with zip built-in function

I want to sort a tuple of integers(in decreasing order) and I then want to save a tuple with the order of indices after sorting in a set. The following piece of code does the job:
my_set = set()
l = (1, 3, 6, 10, 15, 21)
my_set.add(list(zip(*sorted(enumerate(l), key=lambda x: x[1], reverse=True)))[0])
If I evaluate my_set I now have {(5, 4, 3, 2, 1, 0)}
I was trying to do the same with the following code(i for indices, v for values):
my_set.add(i for i, v in zip(*sorted(enumerate(l), key=lambda x: x[1], reverse=True)))
It doesn't work in the same way. The set becomes { <generator object <genexpr> at 0x7f82e49ce360>}
Why is it that if I feed the zip result into a list I can access the tuples inside but I can't use the other syntax?
Is there an alternative way of obtaining some tuple created by zip without having to feed into a list and then indexing into it?
Look at the output that zip returns.
>>> list(zip(*sorted(enumerate(l), key=lambda x: x[1], reverse=True)))
[(5, 4, 3, 2, 1, 0), (21, 15, 10, 6, 3, 1)]
It's always going to be a list of 2 tuples - one tuple being the argsorted indices, and the other being the actual sorted items.
First up, you don't realise this because you're hashing a generator inside a set... but when you decide to exhaust the generator, be prepared for a
ValueError: too many values to unpack (expected 2)
In summary, you're iterating over zip incorrectly. You don't even need to iterate over zip, if it's just the first tuple you're interested in.
What you should instead do, is use next;
>>> my_set.add(next(zip(*sorted(enumerate(l), key=lambda x: x[1], reverse=True))))
>>> my_set
{(5, 4, 3, 2, 1, 0)}
Which gets you just the first tuple.

Convert a dictionary into a list of tuples

Given a dictionary like this:
dic = {(7, 3): 18.51, (1, 3): 18.751, (5, 6): 34.917, (9, 8): 18.9738}
I want to convert it to a list of tuples like this:
my_list = [(7, 3, 18.51), (1, 3, 18.751), (5, 6, 34.917), (9, 8, 18.9738)]
I could have used a loop but I wonder if there is a neat way to do so instead of loops.
Simply use list(..) on some generator:
my_list = list(key+(val,) for key,val in dic.items())
This works since:
list(..) takes as input an iterable and converts it to a list; and
key+(val,) for key,val dic.items() is a generator that takes a pair of key-values of dic and transforms it into a tuple appending the val to the key.
Since we use a generator for a list, we can simplify this with list comprehension:
my_list = [key+(val,) for key,val in dic.items()]
Finally mind that the order in which the tuples occur is not fixed this is because the order how a dict stores elements is not fixed as well.

For each in list Python

Consider I have a tuple in Python
numbers = (('One', 1), ('Two', 2), ('Three', 3), ('Four', 4))
I want to create a numbers_dict in dictionary
numbers_dict = {}
for number in numbers:
numbers_dict[number[0]] = number[1]
and so I get
numbers_dict = {
'One': 1,
'Two': 2,
'Three': 3,
'Four': 4,
}
Can I do something simpler using the syntax
numbers_dict = [n[0] : n[1] for n in numbers]
?
Just pass numbers directly to the dict() type:
numbers_dict = dict(numbers)
From the documentation:
If a positional argument is given and it is a mapping object, a dictionary is created with the same key-value pairs as the mapping object. Otherwise, the positional argument must be an iterable object. Each item in the iterable must itself be an iterable with exactly two objects. The first object of each item becomes a key in the new dictionary, and the second object the corresponding value.
Your numbers tuple is such an iterable, and each element in it is a (key, value) pair.
Demo:
>>> numbers = (('One', 1), ('Two', 2), ('Three', 3), ('Four', 4))
>>> dict(numbers)
{'One': 1, 'Four': 4, 'Three': 3, 'Two': 2}
You could use a dictionary comprehension too, but that's really overkill here; only use that when you do something more meaningful for each key or value. But for completeness sake, that'd look like this:
numbers_dict = {k: v for k, v in numbers}

Categories