Convert hashmap to hash-string-map in Clojure [duplicate] - data-structures
This question already has answers here:
How to convert a clojure keyword into a string?
(5 answers)
Closed 2 months ago.
I have the following data structure:
{:a {:x 1 :y 2} :b {:w 1 :z 2} :c {:m 1 :n 2}}
How can I transform that into a string-hash-map?
I want the following data structure:
{"a" {:x 1 :y 2} 1 {:w 1 :z 2} 2 "c" {:m 1 :n 2}}
(let [data {:a {:x 1 :y 2} :b {:w 1 :z 2} :c {:m 1 :n 2}}]
(update-keys data name))
=> {"a" {:x 1, :y 2}, "b" {:w 1, :z 2}, "c" {:m 1, :n 2}}
Related
Calling up rand-int into a variable
I need to create random numbers and store them into a variable for multiple calculations and for different calls. (defn position (def x (rand-int 2147483647)) (def y (rand-int 2147483647)) (def z (rand-int 2147483647)) ) What I want to do is calling this function in a loop, do calculations with it and store this away. Anyone could help, please? There is probably a better way.
If you want an arbitrary number of things, you need to use an arbitrarily sized data structure. In this case you can probably use the function repeatedly: (repeatedly 5 #(rand-int 2147483647)) In this example we take 5 elements (you can change to as many as you need) from repeatedly running the anonymous no-arguments function #(rand-int 2147483647) which is what you seem to need. To generate an infinite lazy sequence of random ints you can use: (repeatedly #(rand-int 2147483647)) To generate many positions you can also use repeatedly: (defn rand-position [] {:x (rand-int 2147483647) :y (rand-int 2147483647) :z (rand-int 2147483647)}) (def positions (repeatedly rand-position)) (take 5 positions) ;; will generate 5 random positions, each represented as a map
With functional programming, I won't suggest you go down the path of defining global variables (using def). It is better to design your functions to operate on some data structure and returns the same or yet other data structure. In your case, the data-structure is called position. Given it is not a primitive (like integer), you have to decide how to model it. With Clojure, you can either pick a vector or a map for it then all your functions have to follow. Here is how I would go through this development process: Warning: long reply ahead... Prologue (require '[clojure.spec.alpha :as s]) (require '[clojure.spec.gen.alpha :as gen]) (def gen-samples "A function to create a generator, and to generate some samples from it in one step" (comp gen/sample s/gen)) Spec out your data structure ;; dimension is an integer between 0 to 2147483647 (s/def ::dim (s/int-in 0 2147483647)) (gen-samples ::dim) ;; => (1 0 2 1 0 0 2 0 0 27) ;; Option 1: position as a collection of 3 dimensions (s/def ::position (s/coll-of ::dim :count 3)) (gen-samples ::position) ;; => ([0 0 0] [0 0 0] [1 0 0] [0 1 1] [1 0 1] [3 2 1] [26 1 0] [7 1 1] [6 24 1] [2 0 21]) ;; Option 2: position as a map - with x,y,z as keys and with dimension as values (s/def ::x ::dim) (s/def ::y ::dim) (s/def ::z ::dim) (s/def ::position (s/keys :req-un [::x ::y ::z])) (gen-samples ::position) ;; => ({:x 1, :y 1, :z 0} {:x 0, :y 0, :z 0} {:x 1, :y 2, :z 1} {:x 1, :y 2, :z 0} {:x 2, :y 2, :z 5} {:x 4, :y 1, :z 13} {:x 4, :y 8, :z 7} {:x 2, :y 5, :z 10} {:x 22, :y 3, :z 4} {:x 124, :y 1, :z 8}) Assuming you take option 2, now spec out your function ;; in this case, move-east is a function which takes a position ;; and returns another position - with x-dimension of the ;; new position always greater than the old one (s/fdef move-east :args (s/cat :pos ::position) :ret ::position :fn #(> (-> % :ret :x) (-> % :args :pos :x))) Implementation - the easy part (defn move-east [pos] (update pos :x inc)) Some manual test (-> ::position gen-samples first) ;; => {:x 1, :y 1, :z 0} (move-east *1) ;; => {:x 2, :y 1, :z 0} Auto test based on the spec (require '[clojure.spec.test.alpha :as stest]) (-> `move-east stest/check stest/summarize-results) ;; => {:total 1, :check-passed 1} ;; what if my function is wrong? (defn move-east [pos] (update pos :x dec)) (-> `move-east stest/check stest/summarize-results) ;; => {:total 1, :check-failed 1} ;; what is wrong? (-> `move-east stest/check first stest/abbrev-result) ;; which basically returns a result like below... ;; showing return x is -1 and hence fails the ::dim spec {:clojure.spec.alpha/problems ({:path [:ret :x], :pred (clojure.core/fn [%] (clojure.spec.alpha/int-in-range? 0 2147483647 %)), :val -1, :via [:play/dim], :in [:x]}), :clojure.spec.alpha/failure :check-failed}
Ruby - How to use Hash#to_proc?
The documentation is empty, but I want to know how to use it to know when it can be useful.
that is so you can pass a hash proc into something like map. https://bugs.ruby-lang.org/issues/11653 my_hash = ->key{{ a: 1, b: 2, c: 3, d: 4, e: 5, f: 6 }[key]} my_hash[:a] # => 1 [:e, :a, :b, :f, :c, :d].map(&my_hash) # hash is now mappable # => [5, 1, 2, 6, 3, 4]
Clojure thread-first with filter function
I'm having a problem stringing some forms together to do some ETL on a result set from a korma function. I get back from korma sql: ({:id 1 :some_field "asd" :children [{:a 1 :b 2 :c 3} {:a 1 :b 3 :c 4} {:a 2 :b 2 :c 3}] :another_field "qwe"}) I'm looking to filter this result set by getting the "children" where the :a keyword is 1. My attempt: ;mock of korma result (def data '({:id 1 :some_field "asd" :children [{:a 1 :b 2 :c 3} {:a 1 :b 3 :c 4} {:a 2 :b 2 :c 3}] :another_field "qwe"})) (-> data first :children (filter #(= (% :a) 1))) What I'm expecting here is a vector of hashmaps that :a is set to 1, i.e : [{:a 1 :b 2 :c 3} {:a 1 :b 3 :c 4}] However, I'm getting the following error: IllegalArgumentException Don't know how to create ISeq from: xxx.core$eval3145$fn__3146 clojure.lang.RT.seqFrom (RT.java:505) From the error I gather it's trying to create a sequence from a function...though just not able to connect the dots as to why. Further, if I separate the filter function entirely by doing the following: (let [children (-> data first :children)] (filter #(= (% :a) 1) children)) it works. I'm not sure why the first-thread is not applying the filter function, passing in the :children vector as the coll argument. Any and all help much appreciated. Thanks
You want the thread-last macro: (->> data first :children (filter #(= (% :a) 1))) yields ({:a 1, :b 2, :c 3} {:a 1, :b 3, :c 4}) The thread-first macro in your original code is equivalent to writing: (filter (:children (first data)) #(= (% :a) 1)) Which results in an error, because your anonymous function is not a sequence.
The thread-first (->) and thread-last (->>) macros are always problematical in that it is easy to make a mistake in choosing one over the other (or in mixing them up as you have done here). Break down the steps like so: (ns tstclj.core (:use cooljure.core) ; see https://github.com/cloojure/tupelo/ (:gen-class)) (def data [ {:id 1 :some_field "asd" :children [ {:a 1 :b 2 :c 3} {:a 1 :b 3 :c 4} {:a 2 :b 2 :c 3} ] :another_field "qwe"} ] ) (def v1 (first data)) (def v2 (:children v1)) (def v3 (filter #(= (% :a) 1) v2)) (spyx v1) ; from tupelo.core/spyx (spyx v2) (spyx v3) You will get results like: v1 => {:children [{:c 3, :b 2, :a 1} {:c 4, :b 3, :a 1} {:c 3, :b 2, :a 2}], :another_field "qwe", :id 1, :some_field "asd"} v2 => [{:c 3, :b 2, :a 1} {:c 4, :b 3, :a 1} {:c 3, :b 2, :a 2}] v3 => ({:c 3, :b 2, :a 1} {:c 4, :b 3, :a 1}) which is what you desired. The problem is that you really needed to use thread-last for the filter form. The most reliable way of avoiding this problem is to always be explicit and use the Clojure as-> threading form, or, even better, it-> from the Tupelo library: (def result (it-> data (first it) (:children it) (filter #(= (% :a) 1) it))) By using thread-first, you accidentally wrote the equivalent of this: (def result (it-> data (first it) (:children it) (filter it #(= (% :a) 1)))) and the error reflects the fact that the function #(= (% :a) 1) can't be cast into a seq. Sometimes, it pays to use a let form and give names to the intermediate results: (let [result-map (first data) children-vec (:children result-map) a1-maps (filter #(= (% :a) 1) children-vec) ] (spyx a1-maps)) ;;-> a1-maps => ({:c 3, :b 2, :a 1} {:c 4, :b 3, :a 1}) We could also look at either of the two previous solutions and notice that the output of each stage is used as the last argument to the next function in the pipeline. Thus, we could also solve it with thread-last: (def result3 (->> data first :children (filter #(= (% :a) 1)))) (spyx result3) ;;-> result3 => ({:c 3, :b 2, :a 1} {:c 4, :b 3, :a 1}) Unless your processing chain is very simple, I find it is just about always clearer to use the it-> form to be explicit about how the intermediate value should be used by each stage of the pipeline.
I'm not sure why the first-thread is not applying the filter function, passing in the :children vector as the coll argument. This is precisely what the thread-first macro does. From the clojuredocs.org: Threads the expr through the forms. Inserts x as the second item in the first form, making a list of it if it is not a list already. So, in your case the application of filter ends up being: (filter [...] #(= (% :a) 1)) If you must use thread-first (instead of thread-last), then you can get around this by partially applying filter and its predicate: (-> data first :children ((partial filter #(= (:a %) 1))) vec) ; [{:a 1, :b 2, :c 3} {:a 1, :b 3, :c 4}]
How do you check an array for a range in Ruby?
I'm writing a poker program, and I can't figure out how to handle straights. Straight: All cards in a hand of 5 cards are consecutive values. ex. 2..6, 3..7, 4..8, 5..9, 6..T, 7..J, 8..Q, 9..K, T..A cards = [2, 3, 4, 5, 6, 7, 8, 9, "T", "J", "Q", "K", "A"] How can I check a hand, which is an array, for these combinations? Preferably I can check it to see if it's 5 in a row in the cards array.
Edit 2: This is my absolutely final solution: require 'set' STRAIGHTS = ['A',*2..9,'T','J','Q','K','A'].each_cons(5).map(&:to_set) #=> [#<Set: {"A", 2, 3, 4, 5}>, #<Set: {2, 3, 4, 5, 6}>, # ...#<Set: {9, "T", "J", "Q", "K"}>, #<Set: {"T", "J", "Q", "K", "A"}>] def straight?(hand) STRAIGHTS.include?(hand.to_set) end STRAIGHTS.include?([6,3,4,5,2].to_set) # STRAIGHTS.include?(#<Set: {6, 3, 4, 5, 2}>) #=> true straight?([6,5,4,3,2]) #=> true straight?(["T","J","Q","K","A"]) #=> true straight?(["A","K","Q","J","T"]) #=> true straight?([2,3,4,5,"A"]) #=> true straight?([6,7,8,9,"J"]) #=> false straight?(["J",7,8,9,"T"]) #=> false Edit 1: #mudasobwa upset the apple cart by pointing out that 'A',2,3,4,5 is a valid straight. I believe I've fixed my answer. (I trust he's not going to tell me that 'K','A',2,3,4 is also valid.) I would suggest the following: CARDS = [2, 3, 4, 5, 6, 7, 8, 9, "T", "J", "Q", "K", "A"] STRAIGHTS = CARDS.each_cons(5).to_a #=>[[2, 3, 4, 5, 6], [3, 4, 5, 6, 7], [4, 5, 6, 7, 8], # [5, 6, 7, 8, 9], [6, 7, 8, 9, "T"], [7, 8, 9, "T", "J"], # [8, 9, "T", "J", "Q"], [9, "T", "J", "Q", "K"], # ["T", "J", "Q", "K", "A"]] def straight?(hand) (hand.map {|c| CARDS.index(c)}.sort == [0,1,2,3,12]) || STRAIGHTS.include?(hand.sort {|a,b| CARDS.index(a) <=> CARDS.index(b)}) end
If we map each card to a value (9 is 9, "T" is 10, "J" is 11, etc.), then there are two facts that are true of all straights that we can use to solve our problem: All straights have exactly five unique card values The difference between the last and first cards' values is always 4 And so: CARD_VALUES = { 2 => 2, 3 => 3, 4 => 4, 5 => 5, 6 => 6, 7 => 7, 8 => 8, 9 => 9, "T" => 10, "J" => 11, "Q" => 12, "K" => 13, "A" => 14 } def is_straight?(hand) hand_sorted = hand.map {|card| CARD_VALUES[card] } .sort.uniq hand_sorted.size == 5 && (hand_sorted.last - hand_sorted.first) == 4 end This method (1) converts each card to its numeric value with map, then (2) sorts them, and then (3) throws out duplicates with uniq. To illustrate with various hands: hand | 4 A T A 2 | 2 2 3 3 4 | 5 6 4 8 7 | 3 6 2 8 7 ---------+--------------------+--------------------+--------------------+---------------- 1. map | 4 14 10 14 2 | 2 2 3 3 4 | 5 6 4 8 7 | 3 6 2 8 7 2. sort | 2 4 10 14 14 | 2 2 3 3 4 | 4 5 6 7 8 | 2 3 6 7 8 3. uniq | 2 4 10 14 | 2 3 4 | 4 5 6 7 8 | 2 3 6 7 8 Alternatively... I originally posted the following solution, which isn't bad, but is definitely more convoluted: If the hand is sorted, this is easy. You can use Enumerable#each_cons to check each possible straight. CARDS = [ 2, 3, 4, 5, 6, 7, 8, 9, "T", "J", "Q", "K", "A" ] hand = [ 4, 5, 6, 7, 8 ] def is_straight?(hand) CARDS.each_cons(5).any? do |straight| hand == straight end end if is_straight?(hand) puts "Straight!" else puts "Not straight!" end # => Straight! each_cons(5) returns each consecutive set of 5 items, so in the above example hand is first compared to [ 2, 3, 4, 5, 6 ], then [ 3, 4, 5, 6, 7 ], and then [ 4, 5, 6, 7, 8 ], which is a match, so any? returns true. Note that this is not the most efficient solution, but unless you need to check many thousands of hands per second, this is more than adequately performant. If your hands aren't sorted yet, you'll need to do that first. The simplest way to do that is create a Hash that maps cards to a numeric value (as above) and then use sort_by: def sort_hand(hand) hand.sort_by {|card| CARD_VALUES[card] } end hand = [ 4, "A", 2, "A", "T" ] sort_hand(hand) # => [ 2, 4, "T", "A", "A" ]
I did not want to participate, but I can’t keep silence looking at all these oversophisticated solutions around. hand = [2, 5, 7, 'A', 'J'].map(&:to_s) '23456789TJQKA' =~ hand.sort_by{|hc| '23456789TJQKA'.index(hc)}.join || 'A23456789TJQK' =~ hand.sort_by{|hc| 'A23456789TJQK'.index(hc)}.join In a not lame hardcoded manner: suit = '23456789TJQKA' suit =~ hand.sort_by{|hc| suit.index(hc)}.join || suit.rotate(-1) =~ hand.sort_by{|hc| suit.rotate(-1).index(hc)}.join
Generate list of valid hands: valid_hands = cards[0..8].each_with_index.map{|b,i| cards[i..i+4]} #=> [[2, 3, 4, 5, 6], [3, 4, 5, 6, 7], [4, 5, 6, 7, 8], [5, 6, 7, 8, 9], [6, 7, 8, 9, "T"], [7, 8, 9, "T", "J"], [8, 9, "T", "J", "Q"], [9, "T", "J", "Q", "K"], ["T", "J", "Q", "K", "A"]] Once you have the list of all valid hands, you can now check if provided hand is among any? of them (valid ones) or not: if valid_hands.any? { |h| (h - hand).empty? } puts "Valid hand" else puts "Not Valid" end UPDATE In-case 2, 3, 4, 5, "A", 2, 3, 4, "K", "A", 2, 3, "Q", "K", "A", 2, "J", "Q", "K", "A" are also considered as valid hands, calculate them as follows: valid_hands = cards.each_with_index.map { |b,i| i < 9 ? cards[i..i+4] : cards[0..i-9] + cards[i..-1] } # => [[2, 3, 4, 5, 6], [3, 4, 5, 6, 7], [4, 5, 6, 7, 8], [5, 6, 7, 8, 9], [6, 7, 8, 9, "T"], [7, 8, 9, "T", "J"], [8, 9, "T", "J", "Q"], [9, "T", "J", "Q", "K"], ["T", "J", "Q", "K", "A"], [2, "J", "Q", "K", "A"], [2, 3, "Q", "K", "A"], [2, 3, 4, "K", "A"], [2, 3, 4, 5, "A"]]
I recommend writing classes to represent a Card (and maybe Deck and Hand too). Aim for an interface like this: deck = Deck.new.shuffle! hand = Hand.new(deck.draw 5) hand.straight? #=>false puts hand 8♣ 8♦ T♠ 2♦ 7♦ The encapsulation of functionality gives you readability and makes it easy to extend (i.e. with suits) Here's a more simplistic version, implemented as a single Card class. I did add suits though. class Card include Enumerable #enables sorting attr_accessor :value, :suit #values = [2, 3, 4, 5, 6, 7, 8, 9, "T", "J", "Q", "K", "A"] #suits = ["♣","♦","♥","♠"] def self.all #values.product(#suits).map{|c| Card.new c} end def self.straight?(cards) ["A", *#values].each_cons(5).include?(cards.map(&:value)) end def self.flush?(cards) cards.map(&:suit).uniq.size == 1 end def initialize(v) #value, #suit = *v end def <=>(other) #for sorting #values.index(value) <=> #values.index(other.value) end def to_s "#{value}#{suit}" end end This works as follows deck = Card.all puts deck #=> 2♣ 2♦ 2♥ 2♠ 3♣ 3♦ 3♥ 3♠ 4♣ 4♦ 4♥ 4♠ 5♣ 5♦ 5♥ 5♠ 6♣ 6♦ 6♥ 6♠ 7♣ 7♦ 7♥ 7♠ 8♣ 8♦ 8♥ 8♠ 9♣ 9♦ 9♥ 9♠ T♣ T♦ T♥ T♠ J♣ J♦ J♥ J♠ Q♣ Q♦ Q♥ Q♠ K♣ K♦ K♥ K♠ A♣ A♦ A♥ A♠ hand = deck.sample 5 puts hand #=> Q♥ 6♦ 2♣ T♠ Q♦ Card.straight?(hand) #=>false
Step 0: Let's start with an empty class class CardUtils end Step 1: Store values of card in Hash Hash allows fast referencing of values of a card. ##card_values = { 'A' => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 5, 6 => 6, 7 => 7, 8 => 8, 9 => 9, 'T' => 10, 'J' => 11, 'Q' => 12, 'K' => 13 } Thus, you can reference the card value simply as below. ##card_values['A'] # => 1 ##card_values[8] # => 8 Step 2: Sort the hand Apply sort! method to the hand with reference to the card values. def self.sort(hand) hand.sort {|x,y| ##card_values[x] <=> ##card_values[y]} end # => ["A", 2, 3, 4, 5, 6, 7, 8, 9, "T", "J", "Q", "K"] Step 3: Function that tells whether two cards are consecutive def self.is_consecutive(x, y) val_x = ##card_values[x] val_y = ##card_values[y] val_x == val_y - 1 || val_x + 13 == val_y end # is_consecutive('A', 2) # => true # is_consecutive('K', 'A') # => true # is_consecutive('A', 3) # => false Step 4: Check for 'straight' It could be done with simple iteration. def self.has_straight(hand) hand = sort(hand) max_consecutive_count = 0 consecutive_count = 0 hand.each_with_index do |curr, i| prev = hand[i - 1] if is_consecutive(prev, curr) then consecutive_count += 1 else consecutive_count = 0 end if consecutive_count > max_consecutive_count then max_consecutive_count = consecutive_count end end max_consecutive_count >= 5 end # hand = [2, 3, 4, 5, 6, 7, 8, 9, "T", "J", "Q", "K", "A"] # CardUtils.has_straight(hand) # => true Final Result class CardUtils ##card_values = { 'A' => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 5, 6 => 6, 7 => 7, 8 => 8, 9 => 9, 'T' => 10, 'J' => 11, 'Q' => 12, 'K' => 13 } def self.is_consecutive(x, y) val_x = ##card_values[x] val_y = ##card_values[y] val_x == val_y - 1 || val_x + 13 == val_y end def self.sort(hand) hand.sort {|x,y| ##card_values[x] <=> ##card_values[y]} end def self.has_straight(hand) hand = sort(hand) max_consecutive_count = 0 consecutive_count = 0 hand.each_with_index do |curr, i| prev = hand[i - 1] if is_consecutive(prev, curr) then consecutive_count += 1 else consecutive_count = 0 end if consecutive_count > max_consecutive_count then max_consecutive_count = consecutive_count end end max_consecutive_count >= 5 end end
This is how I would write it: hand = [3,4,5,2,'A'] def is_straight(hand) # No need to check further if we do not have 5 unique cards. return false unless hand.uniq.size == 5 # Note the A at beginning AND end to count A as 1 or 14. list_of_straights = 'A23456789TJQKA'.chars.each_cons(5) sorted_hand = hand.map(&:to_s).sort list_of_straights.any? do |straight| straight.sort==sorted_hand end end puts is_straight(hand) #=> true Alternatively if you do not like all the sorting you could exchange the last part to: hand_as_stings = hand.map(&:to_s) list_of_straights.any? do |straight| (straight-hand_as_stings).empty? end
Clojure: I have many sorted maps and want to reduce in order all there values a super maps of keys -> vector
I have seen this but can't work out how to apply it (no pun intended) to my situation. I have a sorted list of maps like this: (note there can be more than two keys in the map) ({name1 3, name2 7}, {name1 35, name2 7}, {name1 0, name2 3}) What I am after is this data structure afterwards: ({:name1 [3,35,0]}, {:name2 [7,7,3]}) Ive been struggling with this for a while and cant seem to get anywhere near. Caveats: The data must stay sorted and I have N keywords not just two.
I'd go for merge-with with some preprocessing added: (def maps [{:a :b :e :f} {:a :c} {:a :d :e :g}]) (apply merge-with concat (for [m maps [k v] m] {k [v]})) >>> {:e (:f :g), :a (:b :c :d)}
I think the function you want is merge-with: user=> (def x {:a 1 :b 2}) user=> (def y {:a 3 :b 4}) user=> (merge-with vector x y) {:a [1 3], :b [2 4]} user=> user=> (def z {:a 5 :b 6 :c 7}) user=> (merge-with vector x y z) {:a [[1 3] 5], :c 7, :b [[2 4] 6]} ; oops user=> (merge-with #(vec (flatten (vector %1 %2))) x y z) {:a [1 3 5] :b [2 4 6] :c 7} user=>
This is my attempt at the problem. It is not as elegant as Rafal's solution. (def maps [{:a :b :e :f} {:a :c} {:a :d :e :g}]) (apply merge-with #(if (list? %1) (conj %1 %2) (list %1 %2)) maps) >>> {:a (:d :b :c), :e (:f :g)}