0

im trying to loop over a set of lists and dicts and pull the correct info from them, should run like:

get random from music, if random is a list then print list, if list contains dict print dict

this is as far as i got before i became confused! please help a noob!

import random
music = ['Band1', 'Band2', 'Band3', 'Band4']
Band1 = ['Album1']
Band2 = ['Album2']
Band3 = ['Album3']
Band4 = ['Album4']
Album1 = {
    "01": 'Track1', "02": 'Track2', "03": 'Track3', "04": 'Track4',
    "05": 'Track5', "06": 'Track6', "07": 'Track7', "08": 'Track8',
    "09": 'Track9', "10": 'Track10', "11": 'Track11'}

i = random.choice(music)
if isinstance(i, list):
    print('is instance')
JONAS402
  • 591
  • 1
  • 9
  • 18
  • 3
    `music` is a list of strings. In what circumstance would it be either a list or a dict? – Daniel Roseman Apr 26 '16 at 10:35
  • First, try to name your variables in a sensible way. E.g. Album1 should really be named something like "tracks_in_album1". You are defining Band1 to Band4 as lists of strings, each with one element. This is probably not what you want... – Sam F Apr 26 '16 at 10:38
  • @SamF thanks Sam, the idea is to have a list of bands, then a list of albums by that band, and a dict of tracks in the album. Then to be able to randomize the band picked but still be able to call on the albums and tracks within. Sorry for my shoddy coding I'm entirely self taught (badly) – JONAS402 Apr 26 '16 at 10:45

3 Answers3

2

I suggest a different data structure:

music = { 
          "Band 1": {
                      "Album A": ["1-Track A1", "1-Track A2", "1-Track A3"],
                      "Album B": ["1-Track B1", "1-Track B2", "1-Track B3"],
                      "Album C": ["1-Track C1", "1-Track C2", "1-Track C3"]
                     },

          "Band 2": {
                      "Album A": ["2-Track A1", "2-Track A2", "2-Track A3"],
                      "Album B": ["2-Track B1", "2-Track B2", "2-Track B3"],
                      "Album C": ["2-Track C1", "2-Track C2", "2-Track C3"]
                     },

          "Band 3": {
                      "Album A": ["3-Track A1", "3-Track A2", "3-Track A3"],
                      "Album B": ["3-Track B1", "3-Track B2", "3-Track B3"],
                      "Album C": ["3-Track C1", "3-Track C2", "3-Track C3"]
                     }
         }

This is a dictionary of bands (key: band name) where each band is a dictionary containing albums (key: album name) where each album is a list containing the track names (index: track number - 1).

Then we can assume that our data structure contains only dictionaries, lists and strings. We want a function that picks a random track, i.e. a string.

Here's a recursive approach. If wanted, it could also be adapted to return the keys and indexes where it found the track as well. It's also capable of any nesting depth, so if you would want to group bands by countries or language or genre etc. that would be no problem.

import random
def pick_track(music_collection):
    # we must pick a key and look that up if we get a dictionary
    if isinstance(music_collection, dict):
        chosen = music_collection[random.choice(list(music_collection.keys()))]
    else:
        chosen = random.choice(music_collection)

    if isinstance(chosen, str):  # it's a string, so it represents a track
        return chosen
    else:  # it's a collection (list or dict) so we have to pick something from inside it
        return pick_track(chosen)

Now we use this method like this to e.g. print 10 random tracks:

for i in range(5):
    print(pick_track(music))

This could output the following example:

1-Track C1
2-Track C3
2-Track A3
3-Track A3
2-Track B1

Update:

You want to also get the keys and indexes where a track was found i.e. the band name, album name and track number? No problem, here's a modified function:

def pick_track2(music_collection):
    if isinstance(music_collection, dict):
        random_key = random.choice(list(music_collection.keys()))
    else:
        random_key = random.randrange(len(music_collection))
    chosen = music_collection[random_key]
    if isinstance(chosen, str): 
        return [random_key, chosen]
    else:
        return [random_key] + pick_track2(chosen)

It now does not return the track name as string, but a list of keys/indices that create the path to the picked track. You would use it like this:

for i in range(5):
    print("Band: '{}' - Album: '{}' - Track {}: '{}'".format(*pick_track2(music)))

An example output:

Band: 'Band 1' - Album: 'Album C' - Track 1: '1-Track C2'
Band: 'Band 2' - Album: 'Album B' - Track 0: '2-Track B1'
Band: 'Band 1' - Album: 'Album B' - Track 0: '1-Track B1'
Band: 'Band 3' - Album: 'Album B' - Track 2: '3-Track B3'
Band: 'Band 3' - Album: 'Album B' - Track 2: '3-Track B3'

See this code running on ideone.com

Byte Commander
  • 6,506
  • 6
  • 44
  • 71
  • i seem to get `TypeError: 'dict_keys' object does not support indexing` when i run pick_track(music) using the supplied code? – JONAS402 Apr 26 '16 at 11:34
  • this works great, but i only want to random pick a band, print band name, then get random album, print album name, then randomget track, print track, is that makes sense? – JONAS402 Apr 26 '16 at 11:39
  • @JONAS402 Check out my updated answer, it now returns the whole lookup path of the track i.e. the dictionary keys and list indices representing band name, album name and track number. – Byte Commander Apr 26 '16 at 11:55
  • my output is `Band: '2' - Album: '-' - Track T: 'r'` `Band: '2' - Album: '-' - Track T: 'r'` `Band: '2' - Album: '-' - Track T: 'r'` `Band: '2' - Album: '-' - Track T: 'r'` `Band: '3' - Album: '-' - Track T: 'r'` – JONAS402 Apr 26 '16 at 12:03
  • Did you use the `pick_track2` function? (sorry, forgot the code formatting... :/) – Byte Commander Apr 26 '16 at 12:16
  • originally no i didnt, but after trying again with `pick_track2(music)` i get no output? – JONAS402 Apr 26 '16 at 12:21
  • You're doing something wrong. Visit the ideone.com link in my question and copy the full program with both functions and examples. That has to work. – Byte Commander Apr 26 '16 at 12:22
  • runs ok on ideone, im currently bludgeoning my way through to get to what i wanted, ill post update soon – JONAS402 Apr 26 '16 at 12:38
1

Twisting your order and using the actual variables (not their names as strings) in your lists should get you started:

Album1 = {
    "01": 'Track1', "02": 'Track2', "03": 'Track3', "04": 'Track4',
    "05": 'Track5', "06": 'Track6', "07": 'Track7', "08": 'Track8',
    "09": 'Track9', "10": 'Track10', "11": 'Track11'
}
Album2 = []
Album3 = ""
Album4 = 0
Band1 = [Album1]
Band2 = [Album2]
Band3 = [Album3]
Band4 = [Album4]
music = [Band1, Band2, Band3, Band4]
user2390182
  • 72,016
  • 6
  • 67
  • 89
0

When I debug this code, I am getting "i" as string. So first you have to obtain the variable by name using globals function.

This code may help you:

import random
music = ['Band1', 'Band2', 'Band3', 'Band4']
Band1 = ['Album1']
Band2 = ['Album2']
Band3 = ['Album3']
Band4 = ['Album4']
Album1 = {
    "01": 'Track1', "02": 'Track2', "03": 'Track3', "04": 'Track4',
    "05": 'Track5', "06": 'Track6', "07": 'Track7', "08": 'Track8',
    "09": 'Track9', "10": 'Track10', "11": 'Track11'}
Album2 = []
Album3 = ""
Album4 = 0

i = random.choice(music)
print i
#val =  eval(i)[0]
#print type(eval(val))

val2 = globals()[i][0]
print type(globals()[val2])
GingerPlusPlus
  • 5,336
  • 1
  • 29
  • 52
Harsha Biyani
  • 7,049
  • 9
  • 37
  • 61
  • Thanks Harsha, I kept getting a string too, could you explain "eval"? I've never used it – JONAS402 Apr 26 '16 at 10:51
  • [Is Using eval In Python A Bad Practice?](http://stackoverflow.com/questions/1832940/is-using-eval-in-python-a-bad-practice), [Security of Python's eval() on untrusted strings?](http://stackoverflow.com/questions/661084/security-of-pythons-eval-on-untrusted-strings), [Why should exec() and eval() be avoided?](http://stackoverflow.com/questions/1933451/why-should-exec-and-eval-be-avoided), [Eval really is dangerous](http://nedbatchelder.com/blog/201206/eval_really_is_dangerous.html) – GingerPlusPlus Apr 26 '16 at 10:57
  • @GingerPlusPlus - oops.. I really dont know that. eval is dangerous.. So can we use global instead? I have updated my code. – Harsha Biyani Apr 26 '16 at 11:06
  • @Harsha: I wouldn't use `globals()` in real code, but in short scripts like this I'd consider it acceptable. It's definitely waaaay better than `eval`. – GingerPlusPlus Apr 26 '16 at 11:09
  • thanks guys, i an unable to test this right now as im using pythontutor to step through the code while im away from my machine, could someone please post output? also is there a recommended alternative to `eval` or `global` ? trying to stick to good practices – JONAS402 Apr 26 '16 at 11:22
  • @JONAS: you can run code online when away from your machine: [rextester](http://rextester.com/l/python3_online_compiler), [repl.it](https://repl.it/languages/python3) – GingerPlusPlus Apr 26 '16 at 11:30
  • @GingerPlusPlus thanks for those, im using pythontutor.com but it lacks functionality, have moved to rextester as repl.it is blocked by my work firewall. thanks again! – JONAS402 Apr 26 '16 at 14:28