a function to check if a sequence has an item

1,881 views
Skip to first unread message

Hiroyuki Fudaba

unread,
May 10, 2016, 10:53:35 AM5/10/16
to Clojure Dev
Hi everyone,

I'm Hiroyuki Fudaba.
I'm wondering if I have a good way of checking a good way to check if a sequence has an item or not, which is an operation we often do.
How the function `contains?` work is pretty confusing when applied to vectors and lists.

Another way is maybe using `some`, like this:
(some #{:a} [:a :b])
; => :a
I personally don't think this is a good solution.
The name `some` don't seem to express the idea of checking an item existence.

To solve this problem, I'd like to propose adding function `has?` to clojure.core.
Function `has` works for seq, list, vector, and map.

Does this seem like something useful?
Do you think a patch for this would be well received?
Should I open a jira issue? 

Thanks,
Hiroyuki Fudaba

Alan Thompson

unread,
May 10, 2016, 1:47:27 PM5/10/16
to cloju...@googlegroups.com

In order to determine if an element is contained in the collection, it may be easiest to use theVector.indexOf() function from java:

(.indexOf (range 10) 5)
;=> 5
(.indexOf [:a :b :c] :b)
;=> 1

Java API Docs are here


--
You received this message because you are subscribed to the Google Groups "Clojure Dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure-dev...@googlegroups.com.
To post to this group, send email to cloju...@googlegroups.com.
Visit this group at https://groups.google.com/group/clojure-dev.
For more options, visit https://groups.google.com/d/optout.

Gary Trakhman

unread,
May 10, 2016, 2:02:02 PM5/10/16
to cloju...@googlegroups.com
I've been using (some #{item} the-seq) for quite a while for this case.

Marshall Bockrath-Vandegrift

unread,
May 10, 2016, 2:08:59 PM5/10/16
to cloju...@googlegroups.com
Clojure sequential collections are also `java.util.Collection`s, which means they implement the `.contains` method:

(.contains [:a :b :c] :a) ;;=> true
(.contains [:a :b :c] :d) ;;=> false

This can be confusing to the uninitiated, but I don't have any ideas for a good fix.

Hiroyuki Fudaba

unread,
May 10, 2016, 11:17:48 PM5/10/16
to Clojure Dev
Thanks for all the comments above.
I now know that there are some ways for each type of sequence.
On the other hand, I found that there seems to be no generic function for this operation.
Of course `some` can be applied to any sequence, but it takes a function as an argument, not an item.
This may be confusing because what I want to do is finding whether a sequence contains the item,
and not an item that satisfies the given predicate.

ArrayMap neither support `.indexOf` nor `.contains`.
Also, it is bothersome to remember which method can be used for a sequence.

Therefore, I would like to add the generic function to clojure.core since it seems to be very useful.

Any comments or suggestions are welcome :-)
Thank you so much.

2016年5月10日火曜日 23時53分35秒 UTC+9 Hiroyuki Fudaba:

Mark Engelberg

unread,
May 11, 2016, 12:34:27 AM5/11/16
to clojure-dev
Word of warning: (some #{item} s) only works if you know for sure the item isn't nil or false.

Other options:
(some #(= item %) s)
(some (partial = item) s)
(some #(contains? #{item} %) s)

There is general consensus that contains? was not a great name choice (probably should have been contains-key?) because this is a FAQ that comes up again and again.  But Clojure values backwards-compatibility too much to make a change to that function name at this point.

It's not entirely clear to me what you are proposing for has?.  Are you intending for it to be O(n) for all collection types, or are you imagining that it would polymorphically select the best way to test containment for each kind of collection?  What would it do for maps, would it check that a key-value pair is in the map, or would it just check that the key is in the map?

Since these answers are not obvious, you can't expect people to be able to predict what has? does.

And that's why the current system is reasonable.  contains? (pretend it was named contains-key?) is a high performance test for maps and sets (and, in theory, can tell you whether an index is among the "keys" of a vector, although I've never actually seen anyone use it that way).  some is a general-purpose tool for sequentially testing elements of a sequence, and the task of finding whether a given item is in the sequence is one of the many things it can do.  No reason to have something more specific when you have a general-purpose tool that can do what you want with only a few keystrokes beyond what it would take with a more dedicated function cluttering up the core namespace.

Not sure how long you've been programming in Clojure, but my experience is that after a while, the (some #{item} s) idiom is so common that you don't give it a second thought, you just instantly interpret that code as looking for item in the sequence s.  So although this is definitely a confusing point that newcomers almost always ask about, it is something most people grow accustomed to.

Mikera

unread,
May 11, 2016, 3:57:05 AM5/11/16
to Clojure Dev
My clojure-utils library has a bunch of handy functions for doing stuff like this.

e.g. 

(if (find-position item coll)
  ... do stuff....)

There are a bunch of optimisations too, e.g. being able to test a Indexed collection (i.e. like vectors) without creating extra garbage by producing a seq.

Herwig Hochleitner

unread,
May 11, 2016, 1:14:32 PM5/11/16
to cloju...@googlegroups.com
Besides your actual proposal: If you need membership as well as sequence, clojure.core/sorted-set-by might suit your needs.​

Tassilo Horn

unread,
May 12, 2016, 3:03:10 PM5/12/16
to Marshall Bockrath-Vandegrift, cloju...@googlegroups.com
Marshall Bockrath-Vandegrift <lla...@gmail.com> writes:

> Clojure sequential collections are also `java.util.Collection`s, which
> means they implement the `.contains` method:
>
> (.contains [:a :b :c] :a) ;;=> true
> (.contains [:a :b :c] :d) ;;=> false
>
> This can be confusing to the uninitiated, but I don't have any ideas
> for a good fix.

I have my own `member?` function which does exactly that.

Bye,
Tassilo

Hiroyuki Fudaba

unread,
May 17, 2016, 10:41:54 PM5/17/16
to Clojure Dev
Thanks for all the comments :)
I agree that my proposal was not clear, thanks for noticing puzzler.

From the comments, I think many people had defined their original function to do the same thing.
I want a single, standard function that everyone uses, which can be applied to sequential collections (not including hashmap).
For example, we can add the following to core or somewhere:

(defn has?
  [s item]
  (.contains s))

2016年5月10日火曜日 23時53分35秒 UTC+9 Hiroyuki Fudaba:
Hi everyone,

Herwig Hochleitner

unread,
May 18, 2016, 10:01:36 PM5/18/16
to cloju...@googlegroups.com
2016-05-18 4:41 GMT+02:00 Hiroyuki Fudaba <deli...@gmail.com>:

I want a single, standard function that everyone uses, which can be applied to sequential collections (not including hashmap).
For example, we can add the following to core or somewhere:

(defn has?
  [s item]
  (.contains s))
 
For me, the slight inconvenience of going through `some` with a predicate is actually welcome, to remind myself that I'm doing an O(n) linear search, instead of the O(1) - O(log32 n), that `contains?` promises, and that I would expect from `has?` as well (due to it being a ?-marked predicate).
The benefit is, that I'm guided towards using a data-structure, that's geared for membership testing, when `=` would be the most important predicate I gave to `some` on a particular collection.

Put another way: I feel your proposal of `has?` provides too little mileage for the API surface it would add to core: When doing a linear search, you should get more out of it, than just the information that an object, you already hold, is in there.
Compare with Mikera's `find-position`: Even that is pretty unlikely for core, since it usually signifies an inefficient algorithm, but at least it gives you back the index it found the element at. This information is very cheap to compute along a linear search and it's potentially very valuable to a caller of `has?`.
Reply all
Reply to author
Forward
0 new messages