141

Given an arbitrary array of size n, I'd like to reorganize the elements of the array based on the array's discrete indices.

Python example:

# Unique array of size n
[ "a", "b", "c", "d", "e", ... <n> ]

# Indices of array
[ 0, 1, 2, 3, 4, ... <index_of_n> ]

# Desired re-organization function 'indexMove'
indexMove(
    [ "a", "b", "c", "d", "e", ... <n> ],
    [ <index_of_n>, 4, 0, 2, 3, ... 1 ]
)

# Desired output from indexMove operation
[ <n>, "e", "a", "c", "d", ... "b" ]

What is the fastest way to perform this operation (achieving the smallest time complexity)?

Abe Hoffman
  • 124
  • 2
  • 11
Niyaz
  • 53,943
  • 55
  • 151
  • 182
  • Probably duplicate of http://stackoverflow.com/questions/976882/shuffling-a-list-of-objects-in-python – kgiannakakis Feb 01 '10 at 15:11
  • 4
    Without specifying *how* you want to order the items its hard to answer. Do you want to sort them? Shuffle them? Remove some of them? – Mizipzor Feb 01 '10 at 15:13
  • @tvanfosson: In this case, arbitrary could also mean: take an arbitrary (but well defined) sort function. – Felix Kling Feb 01 '10 at 15:18
  • 1
    @mizipzor I want to re-order them in a predefined way. (Edited the question to clarify this) – Niyaz Feb 01 '10 at 15:22
  • 1
    @SilentGhost It will have a new index. May be 4. The point is that I know the new order of the items. – Niyaz Feb 01 '10 at 15:24
  • @Niyaz -- wow -- here I am defending your question wording as being adequate to describe your problem and it turns out that your question doesn't even reflect what you are trying to do. It seems that both you and @mizipzor are in desperate need of a dictionary. – tvanfosson Feb 01 '10 at 15:24
  • @hop: Occam razor never fails anyone. It just requires proper use of language as an input, cf. Wittgenstein – SilentGhost Feb 01 '10 at 15:25
  • Hmm.. I voted to close this question as *needing details or clarity*. Not sure why other reviewers overruled it with the *needing debugging details* reason. In any case, it's unclear what the "predefined manner" means in this question. There can be multiple ways to define how one wants to reorder the list: e.g. using a mapping (dict) of indices, using a list, tuples, or some mapping function to sort the list with. This ambiguity led to such different approaches in the answers. – Georgy Jul 17 '20 at 20:58

12 Answers12

289

You can do it like this

mylist = ['a', 'b', 'c', 'd', 'e']
myorder = [3, 2, 0, 1, 4]
mylist = [mylist[i] for i in myorder]
print(mylist)         # prints: ['d', 'c', 'a', 'b', 'e']
Fabs
  • 161
  • 1
  • 9
AJ.
  • 27,586
  • 18
  • 84
  • 94
  • 4
    This creates a new variable. How to re-order a list in-place? Thanks – Confounded Aug 19 '19 at 14:09
  • 6
    @Confounded Simply change the final line to: `mylist[:] = [mylist[i] for i in myorder]` – Adam Mar 01 '20 at 12:35
  • @Adam Thanks! What's the time and space complexity of this operation? Does it iterate over `mylist` when assigning entries from `[mylist[i] for i in myorder]` to `mylist[:]`? – Sean Yun-Shiuan Chuang Oct 01 '20 at 18:05
  • 1
    Hey, I am new to Python and I am curious how does this line of code "[mylist[i] for i in myorder]" work? I have only learnt the basic for loop. – Binaya Thapa Magar Dec 04 '20 at 21:15
  • 1
    Wow, oldie but a goodie. Thanks for taking me back @ZionAdams. Check out List Comprehensions: https://realpython.com/list-comprehension-python/#using-list-comprehensions – AJ. Dec 05 '20 at 02:11
25
>>> a = [1, 2, 3]
>>> a[0], a[2] = a[2], a[0]
>>> a
[3, 2, 1]
SilentGhost
  • 307,395
  • 66
  • 306
  • 293
  • 6
    I just could not understand the syntax until I realized is a [pairwise simultaneous assignment](https://stackoverflow.com/questions/8725673/multiple-assignment-and-evaluation-order-in-python). ´:-) – loved.by.Jesus Apr 27 '20 at 09:56
15
>>> import random
>>> x = [1,2,3,4,5]
>>> random.shuffle(x)
>>> x
[5, 2, 4, 3, 1]
Mark
  • 106,305
  • 20
  • 172
  • 230
  • @wenlibin02, just ran it under 2.7.5 and it still works just fine. Do you get some sort of error? – Mark Jul 20 '15 at 02:44
  • no error, I just type: 1) import random; x = [1, 2, 3]; random.shuffle(x); #it returns None; and 2) I tried np.random.shuffle. the results are the same. – Libin Wen Jul 20 '15 at 07:30
  • 1
    Oh, sorry! I did not realize that I directly change the value of x. It did return None. And it works. Thanks. – Libin Wen Jul 20 '15 at 07:33
6

Is the final order defined by a list of indices ?

>>> items = [1, None, "chicken", int]
>>> order = [3, 0, 1, 2]

>>> ordered_list = [items[i] for i in order]
>>> ordered_list
[<type 'int'>, 1, None, 'chicken']

edit: meh. AJ was faster... How can I reorder a list in python?

Community
  • 1
  • 1
Raphaël Saint-Pierre
  • 2,498
  • 1
  • 19
  • 23
3
>>> a=["a","b","c","d","e"]
>>> a[0],a[3] = a[3],a[0]
>>> a
['d', 'b', 'c', 'a', 'e']
ghostdog74
  • 327,991
  • 56
  • 259
  • 343
3

You can provide your own sort function to list.sort():

The sort() method takes optional arguments for controlling the comparisons.

  • cmp specifies a custom comparison function of two arguments (list items) which should return a negative, zero or positive number depending on whether the first argument is considered smaller than, equal to, or larger than the second argument: cmp=lambda x,y: cmp(x.lower(), y.lower()). The default value is None.

  • key specifies a function of one argument that is used to extract a comparison key from each list element: key=str.lower. The default value is None.

  • reverse is a boolean value. If set to True, then the list elements are sorted as if each comparison were reversed.

In general, the key and reverse conversion processes are much faster than specifying an equivalent cmp function. This is because cmp is called multiple times for each list element while key and reverse touch each element only once.

Community
  • 1
  • 1
Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
3

If you use numpy there's a neat way to do it:

items = np.array(["a","b","c","d"])
indices = np.arange(items.shape[0])
np.random.shuffle(indices)
print(indices)
print(items[indices])

This code returns:

[1 3 2 0]
['b' 'd' 'c' 'a']
3

If you do not care so much about efficiency, you could rely on numpy's array indexing to make it elegant:

a = ['123', 'abc', 456]
order = [2, 0, 1]
a2 = list( np.array(a, dtype=object)[order] )
Shaohua Li
  • 369
  • 3
  • 8
2

One more thing which can be considered is the other interpretation as pointed out by darkless

Code in Python 2.7

Mainly:

  1. Reorder by value - Already solved by AJ above
  2. Reorder by index

    mylist = ['a', 'b', 'c', 'd', 'e']
    myorder = [3, 2, 0, 1, 4]
    
    mylist = sorted(zip(mylist, myorder), key=lambda x: x[1])
    print [item[0] for item in mylist]
    

This will print ['c', 'd', 'b', 'a', 'e']

1

From what I understand of your question, it appears that you want to apply a permutation that you specify on a list. This is done by specifying another list (lets call it p) that holds the indices of the elements of the original list that should appear in the permuted list. You then use p to make a new list by simply substituting the element at each position by that whose index is in that position in p.

def apply_permutation(lst, p):
    return [lst[x] for x in p]

arr=list("abcde")
new_order=[3,2,0,1,4]

print apply_permutation(arr,new_order)

This prints ['d', 'c', 'a', 'b', 'e'].

This actually creates a new list, but it can be trivially modified to permute the original "in place".

MAK
  • 26,140
  • 11
  • 55
  • 86
0
newList = [oldList[3]]
newList.extend(oldList[:3])
newList.extend(oldList[4:])
inspectorG4dget
  • 110,290
  • 27
  • 149
  • 241
0

This is what I used when I stumbled upon this problem.

def order(list_item, i): # reorder at index i
    order_at = list_item.index(i)
    ordered_list = list_item[order_at:] + list_item[:order_at]
    return ordered_list

EX: for the the lowercase letters

order(string.ascii_lowercase, 'h'):
>>> 'hijklmnopqrstuvwxyzabcdefg'

It simply just shifts the list to a specified index

Aaron Conway
  • 127
  • 4
  • 12