I was wondering if there is an elegant way of sorting dict by value in Tcl.
Suppose I have a following dict:
set d1 [dict create k1 10 k2 89 k3 1 k4 15 k5 20]
# Results in dict of form
# k1 => 10
# k2 => 89
# k3 => 1
# k4 => 15
# k5 => 20
Now I want to sort this dictionary so that I have:
# k3 => 1
# k1 => 10
# k4 => 15
# k5 => 20
# k2 => 89
I was hoping there is something similar to Python's sorted().
There is, if you have Tcl 8.6 (this uses the fact that dictionaries can be converted cheaply to and from lists):
set sorted [lsort -integer -stride 2 -index 1 $d1]
If you're still on 8.5 (likely; 8.6 is still in beta) then you need to use several steps:
proc sortDictByValue {dict args} {
set lst {}
dict for {k v} $dict {lappend lst [list $k $v]}
return [concat {*}[lsort -index 1 {*}$args $lst]]
}
set sorted [sortDictByValue $d1]
The -stride option is easier to use, if you've got it.
Related
I am trying to create a function that takes a string in it's parameters. It's supposed to determine the highest and lowest numeric values in the string and return them unchanged.
Here's my code:
def high_and_low(numbers)
numbers.split
numbers.each {|x| x.to_i}
return numbers.max().to_s, numbers.min().to_s
end
Here's the error:
main.rb:5:in `high_and_low': undefined method `each' for "4 5 29 54 4 0 -214 542 -64 1 -3 6 -6":String (NoMethodError)
from main.rb:8:in `<main>'
You have not changed the value from string to array.
Replace numbers.split with numbers = numbers.split.
Also you will need to change from numbers.each { |x| x.to_i } to numbers.map!(&:to_i). Otherwise you don't save integers anywhere.
BTW you don't have to use () and return (if it's in the end) so you can write [numbers.max.to_s, numbers.min.to_s].
Something like this should work:
def high_and_low(numbers)
numbers = numbers.split.map(&:to_i)
[numbers.max, numbers.min].map(&:to_s)
end
high_and_low("4 5 29 54 4 0 -214 542 -64 1 -3 6 -6") #=> ["542", "-214"]
And bonus (one liner, not that you should write code this way):
def high_and_low(numbers)
numbers.split.map(&:to_i).sort.values_at(-1, 0).map(&:to_s)
end
high_and_low("4 5 29 54 4 0 -214 542 -64 1 -3 6 -6") #=> ["542", "-214"]
The other answer is a good approach too so I include it here:
numbers.split.minmax_by { |n| -n.to_i }
Ruby has some nice methods available to make this much more simple:
"2 1 0 -1 -2".split.map(&:to_i).minmax
# => [-2, 2]
Breaking it down:
"2 1 0 -1 -2".split # => ["2", "1", "0", "-1", "-2"]
.map(&:to_i) # => [2, 1, 0, -1, -2]
.minmax # => [-2, 2]
If you want string versions of the values back, compare two integers in a block. minmax will return the values at the corresponding positions in the source array:
"2 1 0 -1 -2".split.minmax{ |a, b| a.to_i <=> b.to_i }
# => ["-2", "2"]
or:
"2 1 0 -1 -2".split.minmax_by{ |a| a.to_i }
# => ["-2", "2"]
minmax and minmax_by do the heavy lifting. The first is faster when there isn't a costly lookup to find the values being compared such as this case where the values are in an array and only needed to_i to compare them.
The *_by version performs a "Schwartzian transform" which basically remembers the values in the block as they're compared so the costly lookup only occurs once. (Many of Enumerable's methods have *_by versions.) These versions of the methods can improve the speed when you want to compare two values that are nested, perhaps in arrays of hashes of hashes, or objects within objects within objects.
Note: When comparing string versions of numbers it's important to convert to a numeric value when comparing. ASCII and strings order differently than numbers, hence the use of to_i.
I know that by Daru::DataFrame#concat one can concatenate dataframes, appending the argument df to the bottom of the caller df.
Now I want to achieve what is df.concat(other, axis=1) in Pandas. In other words, given I have two dataframes where the index are the same, append one df to the right of the other df, resulting df having same index but the concatenated vectors.
Is this possible by some method? Or do I need to iterate and add each columns in for loop?
Is this what you are looking for possibly?
data_frame = data_frame.join(jobs_data_frame, how: :left, on: [:user_id])
You can use add_vector method.
For eg:
2.6.3 :001 > require 'daru'
2.6.3 :007 > df = Daru::DataFrame.new([[00,01,02], [10,11,12],[20,21,22]], order: ["a", "b", "c"])
=> #<Daru::DataFrame(3x3)>
a b c
0 0 10 20
1 1 11 21
2 2 12 22
2.6.3 :008 > df.add_vector("d", [30, 31, 32])
=> [30, 31, 32]
2.6.3 :009 > df
=> #<Daru::DataFrame(3x4)>
a b c d
0 0 10 20 30
1 1 11 21 31
2 2 12 22 32
Although you'll have to add each vector separately.
There is some way to Setting With Enlargement in daru? Something similar to pandas with loc.
Yes you can.
For Daru::Vector objects use the #push method like so:
require 'daru'
v = Daru::Vector.new([1,2,3], index: [:a,:b,:c])
v.push(23, :r)
v
#=>
#<Daru::Vector:74005360 #name = nil #size = 4 >
# nil
# a 1
# b 2
# c 3
# r 23
For setting a new vector in Daru::DataFrame, call the #[]= method with your new name inside the []. You can either assign a Daru::Vector or an Array.
If you assign Daru::Vector, the data will be aligned so that the indexes of the DataFrame and Vector match.
For example,
require 'daru'
df = Daru::DataFrame.new({a: [1,2,3], b: [5,6,7]})
df[:r] = [11,22,33]
df
# =>
#<Daru::DataFrame:73956870 #name = c8a65ffe-217d-43bb-b6f8-50d2530ec053 #size = 3>
# a b r
# 0 1 5 11
# 1 2 6 22
# 2 3 7 33
You assign a row with the DataFrame#row[]= method. For example, using the previous dataframe df:
df.row[:a] = [23,35,2]
df
#=>
#<Daru::DataFrame:73956870 #name = c8a65ffe-217d-43bb-b6f8-50d2530ec053 #size = 4>
# a b r
# 0 1 5 11
# 1 2 6 22
# 2 3 7 33
# a 23 35 2
Assigning a Daru::Vector will align according to the names of the vectors of the Daru::DataFrame.
You can see further details in these notebooks.
Hope this answers your question.
Currently I am storing a combination of a group of items as a single integer (bitmasked value) as in the example below:
Example:
1 - Orange
2 - Banana
4 - Cherry
8 - Apple
And then if the user selects Orange (1) and Apple (8) then the sum of those is 9.
And it is always and only when those two items are combined this value is 9.
And you are able to recover the original two numbers which were used to generate this number.
Here is an example website which does the trick:
http://www.tfxsoft.com/playground/calc.php
What I need:
I need an algoritm (preferrably in Ruby) which would take the sum (9) of those two bitmask values and return the values which it "contains" ( 1 and 4).
I think this might be what you're looking for:
FRUIT = { 1 => 'Orange', 2 => 'Banana', 4 => 'Cherry', 8 => 'Apple' }
def mask_fruit(a, b) a | b end
def unmask_fruit(masked)
FRUIT.select { |k, _| (masked | k) == masked }
end
mask = mask_fruit 1, 8 # => 9
unmask_fruit mask # => {1=>"Orange", 8=>"Apple"}
As I understand you want to find position of bits. Here is simple but not optimal solution:
2.1.5 :033 > 9.to_s(2).reverse.chars.map.with_index { |b, i| b == "1" ? i + 1 : nil }.compact
=> [1, 4]
I have created a dictionary out of two arrays using zip() like
list1 = [1,2,3,4,5]
list2 = [6,7,8,9,19]
dictionary1 = Dict(zip(list1,list2))
Now i want to sort this dictionary by key(list1) or by list2. Can somebody show me a way or function, how to realize it?
Sort also takes a by keyword, which means you can do:
julia> sort(collect(dictionary1), by=x->x[2])
5-element Array{Tuple{Int64,Int64},1}:
(1,6)
(2,7)
(3,8)
(4,9)
(5,19)
Also note that there is a SortedDict in DataStructures.jl, which maintains sort order, and there's an OrderedDict which maintains insertion order. Finally, there's a pull request which would allow direct sorting of OrderedDicts (but I need to finish it up and commit it).
While SortedDict may be useful if it is necessary to keep the dictionary sorted, it is often only necessary to sort the dictionary for output, in which case, the following may be what is required:
list1 = [1,2,3,4,5]
list2 = [6,7,8,9,19]
dictionary1 = Dict(zip(list1,list2))
sort(collect(dictionary1))
... which produces:
5-element Array{(Int64,Int64),1}:
(1,6)
(2,7)
(3,8)
(4,9)
(5,19)
We can sort by values with:
sort(collect(zip(values(dictionary1),keys(dictionary1))))
... which gives:
5-element Array{(Int64,Int64),1}:
(6,1)
(7,2)
(8,3)
(9,4)
(19,5)
The byvalue keyword for the sort function (or sort! for mutating/in place sorting) is useful for sorting dictionaries in order of their values (as opposed to their keys). The result will be of type OrderedDict from OrderedCollections.jl (it's also re-exported by DataStructures.jl).
list1 = [2,1,3,4,5]
list2 = [9,10,8,7,6]
dictionary1 = Dict(zip(list1,list2))
Sort by value (i.e. by list2):
sort(dictionary1; byvalue=true)
Output:
OrderedDict{Int64, Int64} with 5 entries:
5 => 6
4 => 7
3 => 8
2 => 9
1 => 10
Sort by key (i.e. by list1):
sort(dictionary1)
Output:
OrderedDict{Int64, Int64} with 5 entries:
1 => 10
2 => 9
3 => 8
4 => 7
5 => 6