3

How do you find out where all items x are in a n-dimensional list of any shape or size? (Example: [1, 2, [3, [1]]...])

This is the code that I came up with for finding the first item: (Not heavily tested)

def where(x, inputList):
    return _where(x, inputList, [])
def _where(value, inputList, dimension):
    if isinstance(inputList, list):
        i = 0
        for l in inputList:
            dimension.append(i)
            D = _where(value, l, dimension)
            if not D == None:
                return D
            i += 1
            del dimension[-1]
        return None
    else:
        if value == inputList:
            return dimension
        else:
            return None

It recursively checks every item in the list and when it finds the correct one it returns the dimension or the coordinates of that item

Desired Input/Output Example:

x = 1
inputlist = [1, [2, 21], [1]]
o = where_All(x, inputlist)
# o is [[0], [2, 0]]
print inputlist[0]    # is 1
print inputlist[2][0] # is 1

O is a list of coordinates for each item in the list which equals x

GlacierSG
  • 469
  • 4
  • 14
  • 1
    Please give desire input/output in your question. Do your code give wrong answer? if your code is working you might want to ask on [codereview](http://codereview.stackexchange.com/) instead. – wizzup Feb 04 '17 at 13:41
  • Take a look at [this answer](http://stackoverflow.com/a/41778581/4014959). It can handle any combination of nested lists and dictionaries, and it can find all matching items, not just the first one. If you don't need to handle dictionaries you can easily simplify the code. – PM 2Ring Feb 04 '17 at 13:47
  • @wizzup Jökull wants to find _all_ matching items in the nested list. The current code only finds the first one. – PM 2Ring Feb 04 '17 at 13:48
  • 1
    Consider using `numpy.where()` [Documentation](https://docs.scipy.org/doc/numpy/reference/generated/numpy.where.html) – UpSampler Feb 04 '17 at 13:48
  • BTW, your code would be easier for others to read if you used the [PEP-0008](https://www.python.org/dev/peps/pep-0008/) naming conventions. In particular, names beginning with an upper case letter should be used for classes, not for simple variable or functions. The SO syntax highlighter uses black for normal names and cyan for class names, and seeing function and simple variables in the wrong colour is rather confusing. – PM 2Ring Feb 04 '17 at 13:52
  • what do you expect when you search for 1? do you expect `[0, 2]` (i.e. just the index in the topmost list)? Or do you expect `[(0), (2,1,0)]` (i.e. the "coordinates")? – hansaplast Feb 04 '17 at 13:52
  • 2
    @UpSampler I tried using numpy.where() but it only handled very strict structured lists like [[1, 2], [3, 4]] where as I want it to handle unstructured lists like [1, [2, 3, [1]], [1, 3]] – GlacierSG Feb 04 '17 at 13:58
  • @hansaplast I tried clearing that confusion a bit with Desired Input/Output example – GlacierSG Feb 04 '17 at 14:00

1 Answers1

3

If I understand correctly you want to find the coordinates of an element in a nested array. Then you can use the following function which is simpler and makes use of yield which is especially useful when the input (haystack) is an iterator itself (e.g. when reading from a file or similar):

def where(needle, haystack, indexes=[]):
    for i, el in enumerate(haystack):
        if type(el) == list:
            for res in where(needle, el, indexes + [i]):
                yield res 
        elif el == needle:
            yield(indexes + [i])

a = [1, 2, [3, [1]]]
for coords in where(1, a):
    print(coords)

Result:

[0]
[2, 1, 0]
hansaplast
  • 11,007
  • 2
  • 61
  • 75
  • the "yield from" does not seem to work in 2.7 which I am currently using – GlacierSG Feb 04 '17 at 14:10
  • I rewrote the answer for python 2 – hansaplast Feb 04 '17 at 14:14
  • take a look at the answer, it uses many python features you may not be aware of, such as enumerate which increases the `i` value for you so you don't need `i+=1` etc. Also `type(el)` is better suited for non-classes than `isinstance` – hansaplast Feb 04 '17 at 14:16
  • Thank you so much, I will do that – GlacierSG Feb 04 '17 at 14:19
  • 1
    I guess your solution would also work if you just put `yield` instead of `return`. What it does is that when it is called as an iterator (`for i in where..`) yield "returns" the value but then in the next call of where you can continue in your function where you left off – hansaplast Feb 04 '17 at 14:21