3
$\begingroup$

I have a list:

{a,b,c,d,e,f}

And a function sizeFunc[element_] which returns the size {rows,columns} of the element, for example:

In= sizeFunc[c]
Out= {3,3}

To be clear, all elements are symbolic. I want to find the first element in the above list whose size is not {1,1}. So far I tried:

Position[{a,b,c,d,e,f},(sizeFunc[#]=={1,1})&]

The above is just an attempt, I have no idea how to solve this problem. Thanks a lot for helping.

With for loop (what I want to avoid):

list={a,b,c,d,e,f};
For[k = 1, k <= Length[list], k++,
 If[sizeFunc[list[[k]]] != {1, 1},
  firstNonScalark = k; Break[];
 ]
];
$\endgroup$
9
  • $\begingroup$ =!= might be useful. $\endgroup$ Jul 27, 2015 at 20:48
  • $\begingroup$ Do you mean Dimensions[ ]? $\endgroup$ Jul 27, 2015 at 20:48
  • $\begingroup$ @belisarius What do you mean? Dimensions[] has nothing to do with it, I have my own function sizeFunc[] and I'm searching for the first element from the left whose size is not {1,1} $\endgroup$ Jul 27, 2015 at 20:51
  • 1
    $\begingroup$ LengthWhile[list, sizeFunc[#] == {1,1}&]+1. I think For is pretty awful in Mathematica and can always be avoided. If you do need a procedural loop, use Do which at least localizes the iterator, and is more compact and more readable. $\endgroup$
    – Szabolcs
    Jul 27, 2015 at 20:51
  • $\begingroup$ @Szabolcs Thanks it works BUT when no element in a list has size other than {1,1}, the returned value exceeds list dimensions by 1. How do I bound the value to the length of the list? $\endgroup$ Jul 27, 2015 at 21:01

3 Answers 3

2
$\begingroup$

I'll define a sizeFunc to play with:

Clear[sizeFunc]
sizeFunc[a] = {1, 1}; sizeFunc[b] = {1, 1}; sizeFunc[c] = {3, 2}; 
sizeFunc[d] = {2, 4}; sizeFunc[e] = {1, 1}; sizeFunc[f] = {1, 1};

UPDATE:

OP mentioned the desired behavior when all elements return {1, 1}. Taking that into consideration, one can define the following function:

firstnonscalar[l_List] := Module[
  {position},
  If[(position = First@FirstPosition[l, el_ /; sizeFunc[el] != {1, 1}]) != "NotFound",
   position,
   Length[l]
  ]
 ]

As requested, this will return the position of the first element whose sizeFunc does not return {1, 1}, or alternatively the Length of the list, which is the position of the last element.


Original answer:

In my understanding of your question, you want the (position of) the first element whose sizeFunc is not {1,1}.

If you want the element itself, then the following would work:

SelectFirst[{a, b, c, d, e}, sizeFunc[#] != {1, 1} &]
(* Out: c *)

If you want the position of that element in the list, then the following would work instead:

First@FirstPosition[{a, b, c, d, e}, el_ /; sizeFunc[el] != {1, 1}]
(* Out: 3 *)

Here is what happens to these functions if there are no elements for which sizeFunc is different from {1, 1}:

Clear[sizeFunc]
sizeFunc[a] = {1, 1}; sizeFunc[b] = {1, 1}; sizeFunc[c] = {1, 1}; 
sizeFunc[d] = {1, 1}; sizeFunc[e] = {1, 1}; sizeFunc[f] = {1, 1};
SelectFirst[{a, b, c, d, e}, sizeFunc[#] != {1, 1} &]
First@FirstPosition[{a, b, c, d, e}, el_ /; sizeFunc[el] != {1, 1}]

(* Out:
Missing["NotFound"]
"NotFound"
*)

You didn't specify what to do in that case, so I'll leave the handling of those cases to whatever is best to your application.

$\endgroup$
5
  • $\begingroup$ Thanks a lot. If you want to modify your answer, the handling should be to output the position of the last element of the list. $\endgroup$ Jul 27, 2015 at 21:27
  • $\begingroup$ Upvote for FirstPosition. I always feel silly putting those two 1s in arguments to Position and wonder why levelspec isn't the last argument. $\endgroup$
    – Ian
    Jul 27, 2015 at 21:30
  • $\begingroup$ @space_voyager OK thanks for the clarification; I included that in an ad hoc function. $\endgroup$
    – MarcoB
    Jul 27, 2015 at 21:41
  • $\begingroup$ @Ian Thank you. To be honest, I find the syntax and output of Position rather confusing myself, so I often have to look up to get just right. $\endgroup$
    – MarcoB
    Jul 27, 2015 at 21:42
  • $\begingroup$ @MarcoB thanks a lot! $\endgroup$ Jul 27, 2015 at 22:00
2
$\begingroup$

If your size function behaves something like this:

sizeFunc[x_] := {1, 1}
sizeFunc[d] := {3, 2, 1}

then Select can get you the first element matching your negative criterion. Like this:

Select[{a, b, c, d, e, f}, sizeFunc[#]!={1, 1} &, 1]

If you want to use Position to get the position of the element in the list (rather than the element itself), you have to supply a pattern rather than a criterion.

Position[{a, b, c, d, e, f}, x_ /; sizeFunc[x] != {1, 1}, 1, 1]
$\endgroup$
1
  • $\begingroup$ Position[{a, b, c, d, e, f}, x_ /; sizeFunc[x] != {1, 1}, 1]]+1 did the trick! Thank you. $\endgroup$ Jul 27, 2015 at 21:26
2
$\begingroup$

FirstPosition allows for a default value:

sizeFunc[a | b | d | e | f] = {1, 1};
sizeFunc[c] = {2, 3};

list = {a, b, c, d, e, f};

FirstPosition[list, _?(sizeFunc[#] != {1, 1} &), {Length @ list}]
{3}
sizeFunc[c] = {1, 1};

FirstPosition[list, _?(sizeFunc[#] != {1, 1} &), {Length @ list}]
{6}

Without FirstPosition one might use:

Position[list, _?(sizeFunc[#] != {1, 1} &), 1, 1] /.
  {{p_List} :> p, {} :> {Length@list}}
$\endgroup$
2
  • $\begingroup$ I didn't know that! Thank you for pointing that out. That makes for a nice terse one-liner. (+1) $\endgroup$
    – MarcoB
    Jul 28, 2015 at 4:42
  • $\begingroup$ @MarcoB Thanks for the link correction! I also added a method using Position that is reasonably clean. $\endgroup$
    – Mr.Wizard
    Jul 28, 2015 at 15:33

Your Answer

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

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