Function Repository Resource:

SelectDiscard

Source Notebook

Separate a list into two lists based on a given condition

Contributed by: Richard Hennigan (Wolfram Research)

ResourceFunction["SelectDiscard"][list,crit]

gives the pair {list1,list2} where list1 contains all elements ei of list for which crit[ei] is True and list2 contains the rest.

ResourceFunction["SelectDiscard"][list,crit,n]

limits list1 to n elements.

ResourceFunction["SelectDiscard"][crit]

represents an operator form of ResourceFunction["SelectDiscard"] that can be applied to an expression.

Details

The list1 can be thought of as the "selected" elements, and list2 as the "discarded" elements.
The values ei in list2 do not necessarily give False for crit[ei]; they are simply the elements not included in list1.
The object list can have any head, not necessarily List.
When used on an Association, ResourceFunction["SelectDiscard"] picks out elements according to their values.
The order of elements returned in list1 and list2 are not guaranteed.
ResourceFunction["SelectDiscard"] can be used on SparseArray objects.
ResourceFunction["SelectDiscard"][crit][list] is equivalent to ResourceFunction["SelectDiscard"][list,crit].

Examples

Basic Examples (5) 

Separate elements that are even:

In[1]:=
ResourceFunction["SelectDiscard"][{1, 2, 4, 7, 6, 2}, EvenQ]
Out[1]=

Use a pure function to test each element:

In[2]:=
ResourceFunction["SelectDiscard"][{1, 2, 4, 7, 6, 2}, # > 2 &]
Out[2]=

Select only the first expression that satisfies the condition:

In[3]:=
ResourceFunction["SelectDiscard"][{1, 2, 4, 7, 6, 2}, # > 2 &, 1]
Out[3]=

Use the operator form of SelectDiscard:

In[4]:=
ResourceFunction["SelectDiscard"][EvenQ][{1, 2, 4, 7, 6, 2}]
Out[4]=

SelectDiscard operates on values in an Association:

In[5]:=
ResourceFunction[
 "SelectDiscard"][<|a -> 1, b -> 2, c -> 3, d -> 4|>, # > 2 &]
Out[5]=

Scope (6) 

SelectDiscard gives elements for which applying the criterion explicitly yields True in the first list and everything else in the second:

In[6]:=
ResourceFunction["SelectDiscard"][{1, 2, 4, 7, x}, # > 2 &]
Out[6]=

Applying the criterion to the symbolic object x does not explicitly yield True:

In[7]:=
x > 2
Out[7]=

Find pairs containing x:

In[8]:=
ResourceFunction[
 "SelectDiscard"][{{1, y}, {2, x}, {3, x}, {4, z}, {5, x}}, MemberQ[#, x] &]
Out[8]=

Find up to 2 pairs containing x:

In[9]:=
ResourceFunction[
 "SelectDiscard"][{{1, y}, {2, x}, {3, x}, {4, z}, {5, x}}, MemberQ[#, x] &, 2]
Out[9]=

Fewer than the requested elements may be returned:

In[10]:=
ResourceFunction[
 "SelectDiscard"][{{1, y}, {2, x}, {3, x}, {4, z}, {5, x}}, MemberQ[#, z] &, 2]
Out[10]=

Use an operator form as the selection criterion:

In[11]:=
ResourceFunction["SelectDiscard"][Range[10], GreaterThan[3]]
Out[11]=

Use SelectDiscard in operator form:

In[12]:=
Range[10] // ResourceFunction["SelectDiscard"][GreaterThan[3]]
Out[12]=

Separate arguments in Unevaluated code:

In[13]:=
ResourceFunction["SelectDiscard"][
 Unevaluated[1 + 2 + 3 + 4 + 5], OddQ]
Out[13]=

Generalizations & Extensions (4) 

SelectDiscard works with any head, not just List:

In[14]:=
ResourceFunction["SelectDiscard"][f[1, a, 2, b, 3], IntegerQ]
Out[14]=

SelectDiscard works with SparseArray objects:

In[15]:=
s = SparseArray[Table[2^i -> i, {i, 0, 5}]]
Out[15]=
In[16]:=
ResourceFunction["SelectDiscard"][s, EvenQ]
Out[16]=

The result includes a list since only one can be sparse:

In[17]:=
ResourceFunction["SelectDiscard"][s, OddQ]
Out[17]=

Compare to Select:

In[18]:=
Select[s, EvenQ]
Out[18]=
In[19]:=
Select[s, OddQ]
Out[19]=

Separate a Dataset:

In[20]:=
dataset = Dataset[{
   <|"a" -> 1, "b" -> "x", "c" -> {1}|>,
   <|"a" -> 2, "b" -> "y", "c" -> {2, 3}|>,
   <|"a" -> 3, "b" -> "z", "c" -> {3}|>,
   <|"a" -> 4, "b" -> "x", "c" -> {4, 5}|>,
   <|"a" -> 5, "b" -> "y", "c" -> {5, 6, 7}|>,
   <|"a" -> 6, "b" -> "z", "c" -> {}|>}]
Out[20]=
In[21]:=
ResourceFunction["SelectDiscard"][dataset, OddQ[#a] &]
Out[21]=

Separate a Graph by testing its vertices:

In[22]:=
ResourceFunction["SelectDiscard"][\!\(\*
GraphicsBox[
NamespaceBox["NetworkGraphics",
DynamicModuleBox[{Typeset`graph = HoldComplete[
Graph[{1, 2, 3, 4, 5, 6, 7}, {Null, 
SparseArray[
         Automatic, {7, 7}, 0, {1, {{0, 6, 12, 18, 24, 30, 36, 42}, {{2}, {3}, {4}, {
            5}, {6}, {7}, {1}, {3}, {4}, {5}, {6}, {7}, {1}, {2}, {
            4}, {5}, {6}, {7}, {1}, {2}, {3}, {5}, {6}, {7}, {1}, {
            2}, {3}, {4}, {6}, {7}, {1}, {2}, {3}, {4}, {5}, {7}, {
            1}, {2}, {3}, {4}, {5}, {6}}}, Pattern}]}, {GraphLayout -> {"CircularEmbedding", "OptimalOrder" -> False}, VertexLabels -> {"Name"}}]]}, 
TagBox[GraphicsGroupBox[{
{Hue[0.6, 0.7, 0.5], Opacity[0.7], Arrowheads[0.], ArrowBox[CompressedData["
1:eJxTTMoPSmVmYGAQBWImKPa/x1p6mOXl/htvux8t+PzYPuNlBOsco/f7412Z
Hk+oPrMfXX776kLfA0du78+NOb1yz5U3GPLnIPL2pTjksyDm26/CYb47hG9/
EspHl6+NvbFmY+LJPQxg8AHDvejuQ5dHdx+6PLr70OXR3Ycuj+4+dPeg248u
j24/ujy6/ejy6Paj24duPro8uvno8ujmo5uHrh9dHl0/unp0eQBAEHt7
"], 0.022203]}, 
{Hue[0.6, 0.2, 0.8], EdgeForm[{GrayLevel[0], Opacity[
          0.7]}], {DiskBox[{-0.78183, 0.62349}, 0.022203], InsetBox["1", Offset[{-2., 1.5949}, {-0.80161, 0.63926}], ImageScaled[{1., 0.10126}],
BaseStyle->"Graphics"]}, {DiskBox[{-0.97493, -0.22252}, 0.022203], InsetBox["2", Offset[{-2., -0.45649}, {-0.99685, -0.22753}], ImageScaled[{1., 0.61412}],
BaseStyle->"Graphics"]}, {DiskBox[{-0.43388, -0.90097}, 0.022203], InsetBox["3", Offset[{-0.96315, -2.}, {-0.44405, -0.92207}], ImageScaled[{0.74079, 1.}],
BaseStyle->"Graphics"]}, {DiskBox[{0.43388, -0.90097}, 0.022203], InsetBox["4", Offset[{0.96315, -2.}, {0.44405, -0.92207}], ImageScaled[{0.25921, 1.}],
BaseStyle->"Graphics"]}, {DiskBox[{0.97493, -0.22252}, 0.022203], InsetBox["5", Offset[{2., -0.45649}, {0.99685, -0.22753}], ImageScaled[{0., 0.61412}],
BaseStyle->"Graphics"]}, {DiskBox[{0.78183, 0.62349}, 0.022203], InsetBox["6", Offset[{2., 1.5949}, {0.80161, 0.63926}], ImageScaled[{0., 0.10126}],
BaseStyle->"Graphics"]}, {DiskBox[{0., 1.}, 0.022203], InsetBox["7", Offset[{0., 2.}, {0., 1.0222}], ImageScaled[{0.5, 0.}],
BaseStyle->"Graphics"]}}}],
MouseAppearanceTag["NetworkGraphics"]],
AllowKernelInitialization->False]],
DefaultBaseStyle->{"NetworkGraphics", FrontEnd`GraphicsHighlightColor -> Hue[0.8, 1., 0.6]},
FormatType->TraditionalForm,
FrameTicks->None,
ImageSize->{Automatic, 106.17}]\), OddQ]
Out[22]=

Applications (5) 

Select numbers up to 100 that equal 1 modulo both 3 and 5:

In[23]:=
ResourceFunction["SelectDiscard"][Range[100], Mod[#, 3] == 1 && Mod[#, 5] == 1 &]
Out[23]=

Select 4-tuples that read the same in reverse:

In[24]:=
ResourceFunction["SelectDiscard"][
 Tuples[{a, b}, 4], # == Reverse[#] &]
Out[24]=

Select eigenvalues that lie within the unit circle:

In[25]:=
ResourceFunction["SelectDiscard"][Eigenvalues[RandomReal[1, {5, 5}]], Abs[#] < 1 &]
Out[25]=

Separate built-in Wolfram Language symbols based on whether their names contain less than 10 characters:

In[26]:=
{short, long} = ResourceFunction["SelectDiscard"][Names["System`*"], StringLength[#] < 10 &];
In[27]:=
short // Short
Out[27]=
In[28]:=
long // Short
Out[28]=

Select numeric quantities from a product:

In[29]:=
ResourceFunction["SelectDiscard"][7 \[Pi]^2 x^2 y^2, NumericQ]
Out[29]=

Properties & Relations (7) 

The lengths of list1 and list2 will always sum to the length of the original list:

In[30]:=
ResourceFunction["SelectDiscard"][Range[10], PrimeQ]
Out[30]=

When specifying a maximum, the second list can contain elements that satisfy the given condition:

In[31]:=
ResourceFunction["SelectDiscard"][Range[10], PrimeQ, 2]
Out[31]=

SelectDiscard is similar to TakeDrop except it takes/drops elements using the given function:

In[32]:=
list = Range[10]
Out[32]=
In[33]:=
ResourceFunction["SelectDiscard"][list, LessEqualThan[4]]
Out[33]=
In[34]:=
TakeDrop[list, 4]
Out[34]=

For crit that always return True or False, SelectDiscard is very similar to GatherBy:

In[35]:=
ResourceFunction["SelectDiscard"][Range[10], OddQ]
Out[35]=
In[36]:=
GatherBy[Range[10], OddQ]
Out[36]=

However, the first list returned by SelectDiscard always corresponds to elements ei where crit[ei] gives True:

In[37]:=
ResourceFunction["SelectDiscard"][Range[10], EvenQ]
Out[37]=
In[38]:=
GatherBy[Range[10], EvenQ]
Out[38]=

Similar results can be obtained with GroupBy:

In[39]:=
Lookup[GroupBy[Range[10], PrimeQ], {True, False}, {}]
Out[39]=

However, SelectDiscard works on expressions with any head:

In[40]:=
ResourceFunction["SelectDiscard"][g[1, 2, 4, 7, 8], # > 2 &]
Out[40]=

GroupBy does not:

In[41]:=
GroupBy[g[1, 2, 4, 7, 8], # > 2 &]
Out[41]=

SelectDiscard always gives two lists:

In[42]:=
ResourceFunction["SelectDiscard"][{1, 2, 4, 7, x, y}, # > 2 &]
Out[42]=
In[43]:=
ResourceFunction["SelectDiscard"][Range[5], IntegerQ]
Out[43]=

Compare with GatherBy and GroupBy:

In[44]:=
GatherBy[{1, 2, 4, 7, x, y}, # > 2 &]
Out[44]=
In[45]:=
GroupBy[{1, 2, 4, 7, x, y}, # > 2 &]
Out[45]=
In[46]:=
GroupBy[Range[5], IntegerQ]
Out[46]=
In[47]:=
GatherBy[Range[5], IntegerQ]
Out[47]=

Similar results can be obtained with a combination of Select and DeleteElements:

In[48]:=
list = {1, 2, 2, 3, 3, 4};
In[49]:=
ResourceFunction["SelectDiscard"][list, EvenQ]
Out[49]=
In[50]:=
With[{sel = Select[list, EvenQ]}, {sel, DeleteElements[list, sel]}]
Out[50]=

SelectDiscard tends to perform better with large lists:

In[51]:=
list = RandomInteger[100, 10000];
In[52]:=
First[RepeatedTiming[ResourceFunction["SelectDiscard"][list, EvenQ]]]
Out[52]=
In[53]:=
First[RepeatedTiming[
  With[{sel = Select[list, EvenQ]}, {sel, DeleteElements[list, sel]}]]]
Out[53]=

The first list returned by SelectDiscard[list,crit] is equivalent to what's returned by Select[list,crit]:

In[54]:=
list = {-2, 0, 1, 3, 3, x, y};
In[55]:=
First[ResourceFunction["SelectDiscard"][list, Positive]] === Select[list, Positive]
Out[55]=

The second list is effectively equivalent to Select[list,Not@*TrueQ@*crit]:

In[56]:=
Last[ResourceFunction["SelectDiscard"][list, Positive]] === Select[list, Not@*TrueQ@*Positive]
Out[56]=

Possible Issues (1) 

When specifying a limit, the second list can contain elements ei where crit[ei] is True:

In[57]:=
ResourceFunction["SelectDiscard"][{1, 2, 4, 7, 6, 2}, EvenQ, 2]
Out[57]=

Version History

  • 1.0.0 – 05 July 2022

Related Resources

License Information