0
$\begingroup$

Say I have a list of 4 integers valList = { v[1], v[2], v[3], v[4] } whose values may or may not be equal to one another. Now I would like to extract all rows from a matrix such that if the i'th value of valList equals the j'th value of valList then the i'th value of the row should also be equal to the j'th value of that row.

So if, e.g., v[1] == v[2], v[3]==v[4], and v[3]!=v[2] then I want to keep only the four-tuples for which the first value equals the second and the third value equals the fourth. It should still be OK if the third value equals the second.

The idea I had was to write a function that converts the list valList to a criterion for Select but I don't see an elegant way to do this. At the moment I'm using

SelectViaEquality[ list_, mat_ ] :=
   With[{ 
      equalIndices = GatherBy[ Table[ i -> list[[i]], {i, 4}], Last][[;; , ;; , 1]] },
      With[{ 
         crit = Function[ row, 
            And @@ Equal @@@ Map[ Part[ row, #] &, equalIndices, {2} ] 
         ] },
        Select[ crit ] @ mat
      ]
   ];

This function is neither elegant, simple to read, or efficient since the crit function gets redefined with every call...

Question: is there an easy way to extract all rows from a matrix that satisfy the above requirements?

$\endgroup$

1 Answer 1

2
$\begingroup$

If instead of a selector list that was integers you had a selector list that was named patterns, it would work very easily. So, given,

selectPattern1 = {a_, b_, c_, a_};
selectPattern2 = {a_, a_, b_, c_};
selectPattern3 = {a_, b_, b_, a_};
test =
  {{1, 2, 3, 1},
   {1, 2, 3, 4},
   {1, 1, 2, 1},
   {2, 5, 5, 2}}

You'd have:

Cases[test, selectPattern1]
(* {{1, 2, 3, 1}, {1, 1, 2, 1}, {2, 5, 5, 2}} *)

Cases[test, selectPattern2]
(* {{1, 1, 2, 1}} *)

Cases[test, selectPattern3]
(* {{2, 5, 5, 2}} *)

If you must start with a selector that is integers, you can turn it into a selector of patterns. Maybe like this:

selectorPattern4 = 
  Pattern[#, Blank[]] & /@ (Symbol[StringJoin["pat", ToString[#]]] & /@ {1, 1, 2, 3})

Edit

I should comment that this approach isn't strictly speaking applying an equality criterion. I realize I was assuming that since your selector list was integers then your matrix would also be integers. If that's not the case, and you do need equality rather than identical matching, then this won't work.

$\endgroup$
1
  • $\begingroup$ (+1) I first thought this is elegant but probably very slow, but actually it is also very fast. $\endgroup$
    – user293787
    Commented Jul 20, 2022 at 19:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.