Есть ли в Clojure простой способ преобразования между типами списков?

92

Я часто использую ленивый список, когда мне нужен вектор, и наоборот. Также иногда у меня есть вектор карт, когда мне очень нужен набор карт. Есть ли какие-нибудь вспомогательные функции, которые помогут мне преобразовать эти типы?

appshare.co
источник

Ответы:

147

Давайте не будем забывать, что надежный старый intoпозволяет вам взять что угодно seq(список, вектор, карту, набор, отсортированную карту) и пустой контейнер, который вы хотите заполнить, и поместить intoего.

(into [] '(1 2 3 4)) ==> [1 2 3 4]         "have a lazy list and want a vector"
(into #{} [1 2 3 4]) ==> #{1 2 3 4}        "have a vector and want a set"
(into {} #{[1 2] [3 4]}) ==> {3 4, 1 2}    "have a set of vectors want a map"
(into #{} [{1 2} {3 4}]) ==> #{{1 2} {3 4}} "have a vector of maps want a set of maps"

into- это оболочка conj, которая является базовой абстракцией для вставки новых записей в коллекцию в зависимости от типа коллекции. Принцип , который делает этот поток настолько хорошо, что Clojure построен на компонуемых абстракций , в этом случае intoв верхней части conjна верхней части коллекции и seq.

Приведенные выше примеры все равно хорошо составили бы, если бы получатель передавался во время выполнения: поскольку базовые абстракции ( seqи conj) реализованы для всех коллекций (а также для многих коллекций Java), поэтому более высокие абстракции не должны беспокоиться о множестве особых случаев, связанных с данными.

Артур Ульфельдт
источник
3
+1 за в ... стоит отметить, что он также работает с непустыми оригинальными контейнерами (то есть, когда вы хотите добавить в коллекцию)
mikera
11
Также стоит отметить, что из-за intoиспользования conj, выполнение (into '() some-seq)приведет к списку, противоположному some-seq, потому что он conjпопадает в списки.
Чак
Стоит отметить, что intoдля улучшения характеристик производительности используются переходные процессы (для большинства типов последовательностей), чем в большинстве других средств преобразования.
Джарред Хамфри
И теперь он работает с преобразователями, которых не существовало на момент написания этого ответа (я не знаю, были ли транзиенты тоже) (Этот ответ достаточно старый, чтобы записаться в детский сад)
Артур Ульфельдт
33

vec, setи, как правило into, ваши друзья легко «конвертируют» в коллекцию другого типа.

Как вы хотите преобразовать вектор карт в карту карт? Вам нужен ключ, можете ли вы использовать его с примером ввода / ожидаемого вывода?

cgrand
источник
Извините, я имел в виду набор карт .. Я изменил вопрос сейчас
appshare.co
22

Для векторов существует vecфункция

user=> (vec '(1 2 3))
[1 2 3]

Для ленивых последовательностей есть lazy-seqфункция

user=> (lazy-seq [1 2 3])
(1 2 3)

Для конвертации в наборы есть setфункция

user=> (set [{:a :b, :c :d} {:a :b} {:a :b}])
#{{:a :b} {:a :b, :c :d}}
коббал
источник
4
Когда у вас есть что-то неленивое, lazy-seqвместо того, чтобы seqпросто добавить бесполезное косвенное обращение . Если вы действительно хотите вернуть что-то ненулевое, даже за пустую коллекцию, то она есть sequence. lazy-seqэто своего рода конструкция низкого уровня.
cgrand
14

Еще один ответ для преобразования из списка в карту (для полноты) - отсюда :

(apply hash-map '(1 2 3 4))
;=>{1 2, 3 4}
соколиный глаз
источник
9

Чтобы преобразовать вектор в список, вы также можете использовать for, например:

=> (for [i [1 2 3 4]] i)
(1 2 3 4)

Если вы не хотите манипулировать данными, просто используйте seqвектор:

=> (seq [1 2 3])
(1 2 3)
Герт-Ян Хижина
источник
Вместо forтебя можно было просто сделать(map identity [1 2 3 4])
siltalau
7

Преобразовывать вектор в список не нужно . Clojure будет обращаться с вектором так же, как со списком - как с последовательностью - когда последовательность требуется. Например,

user=> (cons 0 [1 2 3])
(0 1 2 3)

Если вам нужно убедиться, что вектор обрабатывается как последовательность, оберните его seq:

user=> (conj [1 2 3] 0) ; treated as a vector
[1 2 3 0]

user=> (conj (seq [1 2 3]) 0) ; treated as a sequence
(0 1 2 3)

Если у вас есть вектор карт, и вам нужен набор карт, не имеет значения, что вектор содержит карты. Вы просто конвертируете вектор в набор как обычно:

user=> (set [{:a 1, :b 2} {"three" 3, "four" 4}])
#{{:a 1, :b 2} {"four" 4, "three" 3}}
Эскиз
источник