1

I've been working on this for too long and need some help. I'm trying to create a dictionary using faker. If it were only that simple. Initially the dictionary is flat. A key and item. If the first letter of the key is 'B' or 'M' it will then turn that string, into a dictionary with 5 keys and keep doing that until it finds none starting with either of those two letters. I know, there's no recursion happening now. That's why I need help. I'm trying to figure out how to properly recurse rather than hard code the depth.

Starting Dictionary:
{
     "Marcia": "https://www.skinner.biz/categories/tags/terms.htm",
     "Nicholas": "https://scott-tran.com/",
     "Christopher": "https://www.ellis.com/",
     "Paul": "https://lopez.com/index/",
     "Jennifer": "https://www.sosa.com/wp-content/main/login.php"
}

Marcia should expand to this...

Example:
    "Marcia": {
        "Alexander": "http://hicks.net/home.html",
        "Barry": {
            "Jared": "https://www.parker-robinson.com/faq.html",
            "Eddie": "https://www.smith-thomas.com/",
            "Ryan": "https://www.phillips.org/homepage/",
            "Mary": {
               "Alex": "http://www.perry.com/tags/explore/post.htm",
               "Joseph": "https://www.hansen.com/main/list/list/index/",
               "Alicia": "https://www.tran.biz/wp-content/explore/posts/",
               "Anna": "http://lee-mclaughlin.biz/search/login/",
               "Kevin": "https://blake.net/main/index/"
            }
           "Evan": "http://carroll.com/homepage.html"
        }
        "Sharon": "https://www.watson.org/categories/app/login/",
        "Hayley": "https://www.parks.com/",
        "William": "https://www.wyatt-ware.com/"
    }

My code is more manual than dynamic in that I must explicitly know now many levels deep the dictionary goes rather than dynamically figuring it out.

Here's what I have that works to the depth of 2 levels but I want to to find any key starting with 'B' or 'M' and acting on it.

import json
from build_a_dictionary import add_dic
from faker import Faker

dic = add_dic(10)
dic1 = {}
dic2 = {}

def build_dic(dic_len):
  dic1 = {}
  fake = Faker()
  if len(dic1) == 0:
    dic1 = add_dic(dic_len)
  print(json.dumps(dic1, indent=4))
  for k, v in dic1.items():
    dic2[k] = add_dic(dic_len)
    for key in dic2[k].keys():
      for f in key:
        if f == 'B' or f == 'M':
          dic2[k][key] = add_dic(dic_len)
  return dic2

Here is the code from add_dic() I wrote:

import string, time
from faker import Faker  #had to install with pip
fake = Faker()
dic = {}
dics = {}
key = ""
def add_dic(x):
  dic={}
  start = time.time()
  if x > 690:
    print("Please select a value under 690")
    sys.exit()
  for n in range(x):
    while len(dic) < x:
      key = fake.first_name()
      if key in dic.keys():
        break
      val = fake.uri()
      dic[key] = val
  end = time.time()
  runtime = end - start
  return dic

3 Answers3

0

You're just doing it wrong, if you want it to be recursive, write the function as a recursive function. It's essentially a custom (recursive) map function for a dictionary. As for your expected dictionary, I'm not sure how you'd ever get Faker to deterministically give you that same output every time. It's random...

Note: There is nothing "dynamic" about this, it's just a recursive map function.

from faker import Faker
import pprint

pp = pprint.PrettyPrinter(indent=4)
fake = Faker()

def map_val(key, val):
    if key[0] == 'M' or key[0] == 'B':
        names = [(fake.first_name(), fake.uri()) for i in range(5)]
        return {k : map_val(k, v) for k,v in names}
    else:
        return val

#uncomment below to generate 5 initial names
#names = [(fake.first_name(), fake.uri()) for i in range(5)]
#initial_dict = {k : v for k,v in names}

initial_dict = {
     "Marcia": "https://www.skinner.biz/categories/tags/terms.htm",
     "Nicholas": "https://scott-tran.com/",
     "Christopher": "https://www.ellis.com/",
     "Paul": "https://lopez.com/index/",
     "Jennifer": "https://www.sosa.com/wp-content/main/login.php"
}

dict_2 = {k : map_val(k,v) for k,v in initial_dict.items()}

pp.pprint(dict_2)

Output:

rpg711$ python nested_dicts.py 

{   'Christopher': 'https://www.ellis.com/',
    'Jennifer': 'https://www.sosa.com/wp-content/main/login.php',
    'Marcia': {   'Chelsea': 'http://francis.org/category.jsp',
                  'Heather': 'http://www.rodgers.com/privacy.jsp',
                  'Jaime': 'https://bates-molina.com/register/',
                  'John': 'http://www.doyle.com/author.htm',
                  'Kimberly': 'https://www.harris.org/homepage/'},
    'Nicholas': 'https://scott-tran.com/',
    'Paul': 'https://lopez.com/index/'
}
TTT
  • 1,952
  • 18
  • 33
  • Thank you rpg711. I'm just practicing here so, getting the same data is not important, however if you add a seedvalue to the faker it will give the same data every time. I really need to study your return statement and the call to map_val. I want to write code like that more. Oh, and populating 'names' is cool. I will make it a point to write something using this style - maybe even this one. – old codgertator Mar 22 '18 at 01:13
0

Thank you all for your help. I've managed to figure it out. It now builds a dynamic dictionary or dynamic json for whatever need.

import sys, json
from faker import Faker
fake = Faker()

def build_dic(dic_len, dic):
  if isinstance(dic, (list, tuple)):
    dic = dict(dic)
  if isinstance(dic, dict):
    for counter in range(len(dic)):
      for k,v in dic.items():
        if k[0] == 'B' or k[0] == "M":
          update = [(fake.first_name(), fake.uri()) for i in range(5)]
          update = dict(update)
          dic.update({k: update})
  return dic

def walk(dic):
  for key, item in dic.items():
      #print(type(item))
      if isinstance(item, dict):
        build_dic(5, item)
        walk(item)
  return dic

a = build_dic(10, ([(fake.first_name(), fake.uri()) for i in range(10)]))
walk(a)
print(json.dumps(a, indent=4))
-1

Recursion is when a function calls itself; when designing a recursive function, it's important to have an exit condition in mind (i.e. when will the recursion stop).

Let's consider a contrived example to increment a number until it reaches a certain value:

def increment_until_equal_to_or_greater_than_value(item, target):
    print 'item is', item,
    if item < target:
        print 'incrementing'
        item += 1
        increment_until_equal_to_or_greater_than_value(item, target)
    else:
        print 'returning'
        return item


increment_until_equal_to_or_greater_than_value(1, 10)

And the output

item is 1 incrementing
item is 2 incrementing
item is 3 incrementing
item is 4 incrementing
item is 5 incrementing
item is 6 incrementing
item is 7 incrementing
item is 8 incrementing
item is 9 incrementing
item is 10 returning

You can see we've defined our recursive part in the if statement and the exit condition in the else.

I've put together a snippet that shows a recursive function on a nested data structure.

It doesn't solve exactly your issue, this way you can learn by dissecting it and making it fit for your use case.

# our recursive method
def deep_do_something_if_string(source, something):
    # if source is a dict, iterate through it's values
    if isinstance(source, dict):
        for v in source.itervalues():
            # call this method on the value
            deep_do_something_if_string(v, something)

    # if source is a list, tuple or set, iterate through it's items
    elif isinstance(source, (list, tuple, set)):
        for v in source:
            deep_do_something_if_string(v, something)

    # otherwise do something with the value
    else:
        return something(source)


# a test something to do with the value
def print_it_out(value):
    print value


# an example data structure
some_dict = {
    'a': 'value a',
    'b': [
        {
            'c': 'value c',
            'd': 'value d',
        },
    ],
    'e': {
        'f': 'value f',
        'g': {
            'h': {
                'i': {
                    'j': 'value j'
                }
            }
        }
    }
}

deep_do_something_if_string(some_dict, print_it_out)

And the output

value a
value c
value d
value j
value f
initialed85
  • 184
  • 6
  • 1
    yuck, using isinstance in python is a no-no except in very few cases. – TTT Mar 21 '18 at 23:26
  • That's an interesting observation, I was unaware- are you able to help me understand why? – initialed85 Mar 21 '18 at 23:29
  • Checking for types in a dynamically typed language is generally bad practice, especially unpythonic in Python. The whole point of a dynamically typed language is that types don't matter, the python interpreter only cares about the names that an object has... lending to much of the power of using such a language in the first place. As a concrete example, see this: https://stackoverflow.com/questions/17028722/is-this-duck-typing-in-python – TTT Mar 21 '18 at 23:32
  • And as an aside, just because an object is of a certain type in python does not mean that it is the same "type" of object as a reference object of that type (at least, not as you would expect in a statically typed language), because python supports dynamic binding of names to instances. – TTT Mar 21 '18 at 23:35
  • So in the example I posted with the recursive function, what would be the correct way to approach that? Simply `for x in thing` won't work for both a dict and a list/set/tuple, as for a dict it'll merely iterate the keys, but at the same time lists etc don't have `itervalues()` Should I be `hasattr(thing, 'itervalues')` or something to decide how to iterate on it? – initialed85 Mar 21 '18 at 23:40
  • Thank you Edward, for your detailed response and then again for this tidbit. I knew it had to be way more simple than I was making it. So right before it calls it's self I could add the check for letters B or M and call it again if so. thank you for your help. I suppose that took 5 minutes to write? I'm still a hack. Is it practice or training I need? – old codgertator Mar 22 '18 at 01:05
  • @oldcodgertator If instead of printing the value in `print_it_out` you did your if 'B' or 'M' check and return the 5-item dictionary as required I think you'd get your desired result; you'd also need to change the calls to .`deep_do_something_if_string(v, something)` to be `v = deep_do_something_if_string(v, something)` And practice makes perfect my friend- for me at least it helps to find a tangible problem to solve, some daily computer frustration that could be automated for example. – initialed85 Mar 22 '18 at 01:48