6
$\begingroup$

I'm trying to select elements of a list that satisfy some criteria using Manipulate[].

Given 2 lists:

list1 = {{a, b, c}, {d, e, f}, {g, h, i}};
list2 = {{9, 0}, {2, 1}, {9.5, 3}};

I want to build a function that, when some criteria are True, will look for them at a specific position in list2. Let me show with an example:

Consider the case in which I only want to find if elements in position 1 of list2 are >=9 and then take the corresponding position from list1. I would write

Pick[list1, Map[#[[1]] >= 9 &, list2]]
(*{{a, b, c}, {g, h, i}}*)

However, if I was to look into elements in position 1 being >=9 and elements of position 2 being <2 I would write

Pick[list1, Map[#[[1]] >= 9 && #[[2]] < 2 &, list2]]
(*{{a, b, c}}*)

But what I want is to have a manipulable function that uses the And function in the second case, but without excluding the possibility that only one of the criterion may be desired. I.e., when only c1 = True I want the first case to be analyzed; when only c2 = True I only want the second case; when both are true, I want the both conditions to be analyzed.

I was thinking about using the following function, but this doesn't work mainly because the new list is not even created.

Manipulate[
 Block[{conditional, condlist, c1, c2},
  condlist = {};
  If[c1, Insert[condlist, #[[1]] > 9, -1]];
  If[c2, Insert[condlist, #[[2]] > 3, -1]];
  condlist

   (*Pick[list1, Map[condlist &, list2]]*)
  ], {c1, {True, False}}, {c2, {True, False}}]

Anyone has any tips on how I can solve this? Thanks in advance!

Edit: though the example I present here only considers 2 conditionals c1 and c2, this is to be applied to a much larger set of conditions. It includes 8 conditionals, and may have any combination among it (e.g. c1 AND c2 AND c4, or c7, or c5 AND c6 AND c7, etc).

$\endgroup$

2 Answers 2

3
$\begingroup$

You could thread Implies over the conditions and functions. In this case, I cheated a little, making the functions incomplete. A boolean Slot expression should be entered into an input field. It is turned into a pure function later, in the code for pickCriteria. I figured you would have to adapt whatever I did to your specific needs anyway, so I just concentrated on getting a working model of the concept.

Use the criteria slider to get however many criteria inputs you wish. Click the checkboxes to turn criteria on or off.

list1 = Range[10];
list2 = RandomInteger[10, 10];
maxn = 10;
pickCriteria[cond_, sel_, l_] := 
  ReleaseHold[And @@ Thread[cond \[Implies] sel]] & /@ l;
Manipulate[
 Grid[{
   list1, 
   Thread@Style[list2, 
     pickCriteria[c, funcs, list2] /. {True -> Red, False -> Gray}],
   Pick[list1, pickCriteria[c, funcs, list2]]
   }],
 {{n, 2, "criteria"}, 2, maxn, 1},
 {{c, ConstantArray[False, maxn]}, None},
 {{funcs, ConstantArray[True, maxn]}, None},
 Dynamic@Grid@Take[
    Transpose[{Array[Checkbox[Dynamic[c[[#]]]] &, maxn], 
      Array[InputField[Dynamic[funcs[[#]]], Hold[Expression]] &, maxn]}],
    n]
 ]

Mathematica graphics

The example in the OP.

list1 = {{a, b, c}, {d, e, f}, {g, h, i}};
list2 = {{9, 0}, {2, 1}, {9.5, 3}};

Mathematica graphics


Update

pickFn below is a slight improvement on pickCriteria above. It also allows for the satisfaction of your desire "to have a manipulable function" rather literally -- see further on.

SeedRandom[2];
list1 = Range[15];
list2 = RandomInteger[10, 15];
maxCrit = 10;

pickFn[cond_, sel_] := Evaluate[And @@ Thread[cond \[Implies] sel]] & /. Hold[x_] :> x;

Manipulate[     
 pickFn[c, funcs],

 {{n, 2, "criteria"}, 2, maxCrit, 1},
 {{c, ConstantArray[False, maxCrit]}, None},
 {{funcs, ConstantArray[True, maxCrit]}, None},
 Dynamic @ Grid @
   Take[Transpose[{Array[Checkbox[Dynamic[c[[#]]]] &, maxCrit], 
                   Array[InputField[Dynamic[funcs[[#]]], Hold[Expression]] &, maxCrit]}],
        n],
 Dynamic @ Framed @
   Grid[{list1,
         Thread @ Style[list2, # /. {True -> Red, False -> Gray}],
         Pick[list1, #]} &[pickFn[c, funcs] /@ list2]]
 ]

We can map the Manipulate expression pickFn[c, funcs] onto another list, via Setting, after manipulating the function into the one we want.

Mathematica graphics

While the OP seems to wish to use the function only inside the Manipulate, I couldn't resist the call for a "manipulable function." :)

$\endgroup$
1
  • $\begingroup$ Well, I think this answer adds to @Kuba's because of the ability to easily add/remove criteria. This makes it very versatile! Nice one! $\endgroup$
    – Sos
    Commented Jul 25, 2013 at 15:34
2
$\begingroup$

I think the answer may depend of how huge lists you are dealing with.

This is my approach:

list1 = {{a, b, c}, {d, e, f}, {g, h, i}};
list2 = {{9, 0}, {2, 1}, {9.5, 3}};

Manipulate[
           fcon := Which[c1 && c2, And, c1, #1 &, c2, #2 &, True, False &];
           cond = {#1 >= 9, #2 < 2} & @@@ list2;
           Pick[list1, fcon @@@ cond]
          , {c1, {True, False}}, {c2, {True, False}}]

Edit - general approach to list of conditions

Without loss of generality I will show it on this example of lists:

mainlist = CharacterRange["A", "J"] (*list to Pick from*)
Range@Length@mainlist (*list to test on*)
tests = {NumericQ, OddQ, # < 5 &} (*tests*)
{"A", "B", "C", "D", "E", "F", "G", "H", "I", "J"}
{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
{NumericQ, OddQ, #1 < 5 &}
Manipulate[

  Column[{
          TogglerBar[Dynamic@c, Thread[Rule[Range@Length@tests, tests]]]
          ,
          Pick[mainlist, toPick]
         }, Center, Spacings -> 5]


 , {c, None}, ContentSize -> {300, 300}, Alignment -> Center,
 Initialization :> (
     c = {};
     mainlist = CharacterRange["A", "J"]; 
     len = Length@mainlist;
     tests = {NumericQ, OddQ, # < 5 &};
     cond = Outer[Compose, tests, Range@len]; (* I like this part very much :)*)
     toPick := If[c === {}, ConstantArray[False,len], And @@@ (Transpose[cond[[c]]])];
 )]

enter image description here

Description:

test - list of tests, I've assumed it is constant but there will be no problem with making it Dynamic

cond - array with data if given element fulfills given condition

toPick - Since you want to consider only Conjuntion of tests we Apply And to only those results of test that are set by TogglerBar.


For the example lists in the OP:

Manipulate[
 Column[
  {

   TogglerBar[Dynamic@con,
    Thread[
     Rule[
      Range@Length@tests, tests]
     ]
    ],
   Pick[mainlist, toPick]
   }, Center, Spacings -> 5
  ],
 {con, None},
 ContentSize -> {300, 300}, Alignment -> Center,
 Initialization :>
  (
   con = {};
   mainlist = list1;
   len = list2;
   tests = {#1[[1]] >= 9 &, #[[2]] < 3 &};
   cond = 
    Outer[Compose, tests, len, 1];(*I like this part very much:)*)

   toPick := If[
     con === {},
     ConstantArray[False, Length@len],
     And @@@ (Transpose[cond[[con]]])];
   )
 ]

enter image description here

$\endgroup$
6
  • $\begingroup$ Thanks for the tips @kuba! Indeed, this I would like to apply this to much larger lists and conditionals. I believe the size of list1 or list2 are not a problem in here, but the number of conditionals may be. I am going to use this to study 8 conditionals which makes it a bit "cumbersome" to write all the possible conditions inside the Which $\endgroup$
    – Sos
    Commented Jul 24, 2013 at 16:43
  • $\begingroup$ @Sosi I have no new ideas about the method but I've added general approach, maybe you will find it useful. $\endgroup$
    – Kuba
    Commented Jul 24, 2013 at 20:50
  • $\begingroup$ indeed it is useful! Thanks! $\endgroup$
    – Sos
    Commented Jul 25, 2013 at 10:34
  • $\begingroup$ @Sosi if you do not want to try many conditions's combinations then you may consider making cond dynamically created. but if you want to check everything and lists are not extremely large you can leave it there. $\endgroup$
    – Kuba
    Commented Jul 25, 2013 at 10:38
  • 1
    $\begingroup$ @Sosi No, it is not look here I'm not used to Outer too but I knew it is going to be near there so I've tried couple of things :P $\endgroup$
    – Kuba
    Commented Jul 25, 2013 at 11:06

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.