6
$\begingroup$

I generated a list made up of lists of four elements such as:

n = {{49, 9, 9, 5}, {57, 13, 9, 5}, {81, 21, 13, 5}, {95, 19, 19, 7},...}

I would like to create a sublist that only contains lists whose elements are all congruent to 1 mod 4. How can I do this?

$\endgroup$

3 Answers 3

8
$\begingroup$

You can write a very explicit and readable code by using Select and AllTrue

n = {{49, 9, 9, 5}, {57, 13, 9, 5}, {81, 21, 13, 5}, {95, 19,
     19, 7}};

Select[n, AllTrue[Mod[#, 4] == 1 &]]

(* {{49, 9, 9, 5}, {57, 13, 9, 5}, {81, 21, 13, 5}} *)

but in my experience, casting the problem in term of Pick gives often the fastest code.

n = RandomInteger[100, {10^5, 4}];

sub1 = Select[n, AllTrue[Mod[#, 4] == 1 &]]; // RepeatedTiming
sub2 = DeleteCases[Pick[n, Mod[n, 4], {1, 1, 1, 1}], {}]; // RepeatedTiming

(* {0.15, Null} *)

(* {0.0700, Null} *)

sub1 === sub2

(*  True *)
$\endgroup$
8
$\begingroup$

Indeed, Pick is very fast. A bit faster is using some integer vector arithmetic to create a scalar condition for picking:

n = RandomInteger[100, {10^2, 5}];
sub1 = Select[n, AllTrue[Mod[#, 4] == 1 &]]; // RepeatedTiming // First
sub2 = DeleteCases[Pick[n, Mod[n, 4], {1, 1, 1, 1}], {}]; // RepeatedTiming // First

sub3 = Pick[n, Mod[n - 1, 4].ConstantArray[1, 4], 0]; // RepeatedTiming // First
sub1 == sub2 == sub3

0.132

0.045

0.0030

True

$\endgroup$
4
  • $\begingroup$ True. If you have time, it's always better to simplify the pattern to a simple expression with no parts like True/False or 1/0. $\endgroup$
    – Batracos
    May 7, 2018 at 8:11
  • $\begingroup$ Note also that Pick[n, Mod[n, 4], {1, 1, 1, 1}] creates an unpacked array and that arrays with True and False cannot be packed. (Btw., I consider this a shortcoming of Mathematica) $\endgroup$ May 7, 2018 at 9:13
  • $\begingroup$ This is because there is no native bit data type. The best you can get is a byte image or a ByteArray but there's not much support for those yet outside their respective domain. $\endgroup$
    – Batracos
    May 7, 2018 at 21:31
  • $\begingroup$ That's what I meant... $\endgroup$ May 7, 2018 at 21:42
3
$\begingroup$

Another way using pattern matching and replacement (probably slower than the other implementations):

n = {{49, 9, 9, 5}, {57, 13, 9, 5}, {81, 21, 13, 5}, {95, 19, 19, 7}};
n /. list_List?(! And @@ Thread[Mod[#, 4] == 1] &) -> Sequence[]
(*{{49, 9, 9, 5}, {57, 13, 9, 5}, {81, 21, 13, 5}}*)

Edit:

One could also use Nothing instead of Sequence[] to represent an element that will automatically be removed.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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