6

I need to remove the reverse versions of lists from list of lists. I.e.: {{1,2},{2,2},{2,1},{a,1,2},{2,1,a}} I need to reduce to {{1,2},{2,2},{a,1,2}}. The requirement to remove only the reverse of a given list means that permutations other than the reverse will be conserved, so that

{{1,2,3},{2,1,3},{3,2,1},{1,2,2},{a,1,2},{2,1,a}}

should reduce to

{{1,2,3},{2,1,3},{1,2,2},{a,1,2}}

My try gives correct result but it use nested For which is not too nice. Does anyone know of a better way to do it?

Peltio
  • 5,516
  • 3
  • 27
  • 27
  • 1
    Do you need to conserve the ordering in the sublists order? If not, you could map Sort on each one of them and then use Union to remove duplicates. As in Union[Sort /@ {{1, 2}, {2, 2}, {2, 1}, {a, 1, 2}}] – Peltio Nov 08 '13 at 13:36
  • @Peltio It's not so easy, {{1,2,3}, {3,1,2}} should not be reduced and it will be with your method. – Kuba Nov 08 '13 at 14:13
  • 1
    @Kuba But the basic idea is sane: find a canonical for for sublists, then use Union for an efficient solution. The question is: what efficiently computable canonical form can we use? (I.e. a way to unambiguously choose either list or Reverse[list] for each item.) – Szabolcs Nov 08 '13 at 14:19
  • @Szabolcs I can agree with you but Peltio's comment is not describing an idea which has to be improved, but the solution which fits only by a coincidence. Or I've missed something again? :) – Kuba Nov 08 '13 at 14:23
  • @Kuba, you're right, I did not consider other permutations. – Peltio Nov 08 '13 at 17:03
  • @Szabolcs If the duplicates of lists themselves should also be deleted, then Kuba's solution is the way to go. If not, then the problem is in principle unsolvable in the single-list canonical form approach, since one has to keep the memory if which version was encountered first (as I did in mine) - which is, it can not be reduced to just per-element canonical form computations. – Leonid Shifrin Nov 08 '13 at 17:38
  • @Artes You was the first, but as you wish.. – Филипп Цветков Nov 12 '13 at 09:31

5 Answers5

8

All right, this will be a linear-time solution:

ClearAll[removeReversed];
removeReversed[l_List] :=
   Module[{f},
     f[x_List] := (f[Reverse[x]] = Sequence[]; x);
     f /@ l]

For example:

lst = {{1, 2}, {2, 2}, {2, 1}, {a, 1, 2}, {2, 1, a}};

then:

removeReversed @ lst

(* {{1, 2}, {2, 2}, {a, 1, 2}} *)
Leonid Shifrin
  • 114,335
  • 15
  • 329
  • 420
  • Actually, this is not quite right... – Leonid Shifrin Nov 08 '13 at 12:46
  • Try this list {{0,0,0,0,0,0}, {1,1,0,0,0,0}, {1,1,1,1,0,0}, {1,1,1,1,1,1}, {1,1,0,1,1,0}, {1,1,0,0,1,1}, {0,1,1,0,0,0}, {0,1,1,1,1,0}, {0,1,1,0,1,1}, {0,0,1,1,0,0}, {0,0,1,1,1,1}, {0,0,0,1,1,0}, {0,0,0,0,1,1}}. Artes' solutions works butter – Филипп Цветков Nov 08 '13 at 12:51
  • 1
    I was thinking about deleting the same elements with my method. I'm almost sure it must be done. Otherwise the filtering depends of main list order. For example: try removeReversed at lst = {{1, 2}, {1, 2}, {2, 1}}; and at Reverse[lst]. – Kuba Nov 08 '13 at 14:17
  • @Kuba Yes, surely it depends on main list order. Why should it not? These are just different problems. In the way the problem is specified now, it does not assume removal of duplicate lists. – Leonid Shifrin Nov 08 '13 at 14:42
  • 1
    Well, it's natural for me it shouldn't unless specified explicitly. Unfortunatelly OP's example does not have this feature so we have to wait to check if you are right. But yes, there is no good reason why it shouldn't. – Kuba Nov 08 '13 at 14:50
  • @Kuba All right, I did not retract my vote for your answer anyway :) – Leonid Shifrin Nov 08 '13 at 15:43
  • @LeonidShifrin This is an instance where a good answer is not well appreciated. Wanted to delete my answer but I think it should rest for educational purposes. Perhaps I should add some benchmarks, but I find this question not quite well focused. – Artes Nov 09 '13 at 19:28
  • @Artes Thanks man, but I think this is the case when the answer gets appreciation slowly, in part because it takes more effort to get the context of both the question, and the answer (than in some other questions like e.g. this one). Besides, there still might be better ones. Re: your answer - you should definitely keep it, in particular for educational purposes (as you mentioned), I think. – Leonid Shifrin Nov 09 '13 at 19:32
  • @ФилиппЦветков Thanks for the accept. But what made you change your mind? – Leonid Shifrin Nov 12 '13 at 09:21
  • @LeonidShifrin "I'd accept Leonid's answer, since it is fast and works better for long lists. – Artes 15 hours ago" – Филипп Цветков Nov 12 '13 at 09:29
  • @ФилиппЦветков Ok, I see. Artes is generous as always. – Leonid Shifrin Nov 12 '13 at 09:33
  • @LeonidShifrin f[x_List] := (f[Reverse[x]] = Sequence[]; x); I've never seen this kind of definition before. What is this? – matheorem Nov 20 '13 at 06:43
  • @matheorem This is a special case of memoization. You can search for "memoization" on this site. Basically, this means that every call to f on a reverse list will result in Sequence[], effectively deleting it. – Leonid Shifrin Nov 20 '13 at 11:24
  • @LeonidShifrin I understand. Thank you! – matheorem Nov 21 '13 at 10:25
4
list = {{1, 2}, {2, 2}, {2, 1}, {a, 1, 2}, {2, 1, a}};

There is an optional test argument in DeleteDuplicates:

DeleteDuplicates[ list, Reverse[#1] == #2 &]
{{1, 2}, {2, 2}, {a, 1, 2}}

alternatively we can use SameTest in Union:

Union[ list, SameTest -> (Reverse[#1] == #2 &)]
Artes
  • 57,212
  • 12
  • 157
  • 245
  • 2
    This is what came to my mind first, too. Alas, it will have quadratic complexity for large lists, because DeleteDuplicates will have to perform pairwise comparisons. – Leonid Shifrin Nov 08 '13 at 12:43
2
GatherBy[list, Sort[{#, Reverse@#}] &][[;; , 1]]

I've deleted this answer because of Leonid's remark but now I think exact duplicates needs to be deleted to, otherwise the filtering depends of the order in main list.

Let's see that with Artes' solution:

list = {{1, 2}, {1, 2}, {2, 1}};
DeleteDuplicates[list, Reverse[#1] == #2 &]
DeleteDuplicates[Reverse@list, Reverse[#1] == #2 &]
{{1, 2}, {1, 2}}
{{2, 1}}
Kuba
  • 136,707
  • 13
  • 279
  • 740
0

Another work around can be having a new list every time with reversed element removed.

   fun[l_List] := 
     Block[{k = l, 
       fin}, {fin = Quiet[Last[Table[If[MemberQ[k, Reverse[k[[i]]]] && 
             Hash[k[[i]]] != Hash[Reverse[k[[i]]]], 
             k = DeleteCases[k, Reverse[k[[i]]]], k], {i, 1, 
             Length[k]}]]]}; fin]


{{1,2,3},{2,1,3},{3,2,1},{1,2,2},{a,1,2},{2,1,a}}

{{1, 2, 3}, {2, 1, 3}, {1, 2, 2}, {a, 1, 2}}

{{0, 0, 0, 0, 0, 0}, {1, 1, 0, 0, 0, 0}, {1, 1, 1, 1, 0, 0}, {1, 1, 1,
    1, 1, 1}, {1, 1, 0, 1, 1, 0}, {1, 1, 0, 0, 1, 1}, {0, 1, 1, 0, 0, 
   0}, {0, 1, 1, 1, 1, 0}, {0, 1, 1, 0, 1, 1}, {0, 0, 1, 1, 0, 0}, {0,
    0, 1, 1, 1, 1}, {0, 0, 0, 1, 1, 0}, {0, 0, 0, 0, 1, 1}}

{{0, 0, 0, 0, 0, 0}, {1, 1, 0, 0, 0, 0}, {1, 1, 1, 1, 0, 0}, {1, 1, 1, 1, 1, 1}, {1, 1, 0, 1, 1, 0}, {1, 1, 0, 0, 1, 1}, {0, 1, 1, 0, 0, 0}, {0, 1, 1, 1, 1, 0}, {0, 0, 1, 1, 0, 0}}

 {{1, 2}, {2, 2}, {2, 1}, {a, 1, 2}, {2, 1, a}}

{{1, 2}, {2, 2}, {a, 1, 2}}

Pankaj Sejwal
  • 2,063
  • 14
  • 23
0

Just a variant (suffering from pairwise comparison growth):

fun[u_List]:=Tally[Union[u],#1===Reverse@#2&][[;;,1]]

Test lists:

list1={{1,2,3},{2,1,3},{3,2,1},{1,2,2},{a,1,2},{2,1,a}}
list2={{1, 2}, {1, 2}, {2, 1}}

Applying:

fun[list1]
fun[list2]

yields:

{{1, 2, 2}, {1, 2, 3}, {2, 1, 3}, {2, 1, a}}

{{1,2}}

To avoid double pairwise comparisons (Union, then Tally):

func[u_]:=Tally[u,#1===#2||#1===Reverse@#2&][[;;,1]]
ubpdqn
  • 60,617
  • 3
  • 59
  • 148