# Delete only when there are two (or more) 1s

I would like to remove element from a nested list when they have a certain number of elements repeated. For example:

{{1,2,3,4},{1,1,2,3},{1,3,4,1},{1,4,2,4}}


I would like to remove the 2nd and 3rd sublists because they have 2 cases of the 1. I would like the end result to remain nested however.

{{1,2,3,4},{1,4,2,4}}


I believe that Delete cases and count will be used to solve this problem.

• So you mean, not "greater than 2", but "greater than or equal to 2"? – Michael E2 Mar 7 '16 at 19:11
• Will it always be 2 or do you want to generalise to an arbitrary threshold? – Martin Ender Mar 7 '16 at 19:11
• I am using 2 as a arbitrary threshold. Also it would be greater than or equal to 2. – Andre Mar 7 '16 at 19:13
• The 4th sublist has two cases of a 4 -- should it be removed, too? – Michael E2 Mar 7 '16 at 19:13
• Select[#, Count[#, 1] < 2 &] &@ {{1, 2, 3, 4}, {1, 1, 2, 3}, {1, 3, 4, 1}, {1, 4, 2, 4}} gives you the list of lists that have "1" not more than 1 time – BlacKow Mar 7 '16 at 19:15

There are a lot of ways to do this. Using Select:

Select[list, Count[#, 1] < 2 &]


Using Cases:

Cases[list, l_ /; Count[l, 1] < 2]


Also if the threshold is at 2, then this DeleteCases version might also be an option (but it gets ugly when you want a greater threshold):

DeleteCases[list, {___, 1, ___, 1, ___}]


As garej points out you can also turn the Cases solution into DeleteCases:

DeleteCases[list, l_ /; Count[l, 1] > 1]


Or use Replace for the pattern matching:

Replace[list, {___, 1, ___, 1, ___} -> Nothing, 1]


However, out of all of these, the pattern matching solutions are actually fastest:

list = RandomInteger[4, {500, 4}];
RepeatedTiming[Select[list, Count[#, 1] < 2 &];]
RepeatedTiming[Cases[list, l_ /; Count[l, 1] < 2];]
RepeatedTiming[DeleteCases[list, l_ /; Count[l, 1] > 1];]
RepeatedTiming[DeleteCases[list, {___, 1, ___, 1, ___}];]
RepeatedTiming[Replace[list, {___, 1, ___, 1, ___} -> Nothing, 1];]

(* {0.000919, Null}
{0.00094, Null}
{0.00093, Null}
{0.00038, Null}
{0.000253, Null} *)


But note that once you go to bigger thresholds, the pattern matching becomes expensive. It's still fastest for some other small thresholds, but around a threshold of 10, the speed is comparable to that of the other three solutions. When you go to a threshold of 20, then the pattern matching is ten times slower than the other solutions.

• would it be possible to add an OR/AND to this and add another criteria – Andre Mar 7 '16 at 19:17
• @Andre In the first two cases, easily. Just use && or || after Count[..., 1] < 2 and your additional criteria. – Martin Ender Mar 7 '16 at 19:18
• As regards the last approach, when the threshold increases one could replace the pattern by Riffle[ConstantArray[___, threshold + 1], 1] – Coolwater Mar 7 '16 at 19:43
• @Coolwater Sure, but I think the pattern matching will become a lot more expensive than a simple Count and it's also pretty obscure code whereas the first solution is very readable. – Martin Ender Mar 7 '16 at 19:44
• @MartinBüttner, why not DeleteCases[data, l_ /; Count[l, 1] > 1] – garej Mar 8 '16 at 8:40