Can't seem to figure this out. Please help me understand what this code is requesting for regarding a variable and what the intended output is supposed to be. Thanks in advance!
def function_name(a)
a.inject({}){ |a,b| a[b] = a[b].to_i + 1; a}.\
reject{ |a,b| b == 1 }.keys
end
Assuming a is an array,
The function first count the occurrences of the keys.
a = ['a', 'b', 'c', 'b']
a.inject({}) { |a,b|
# a: a result hash, this is initially an empty hash (`{}` passed to inject)
# b: each element of the array.
a[b] = a[b].to_i + 1 # Increase count of the item
a # The return value of this block is used as `a` argument of the block
# in the next iteration.
}
# => {"a"=>1, "b"=>2, "c"=>1}
Then, it filter items that occur multiple times:
...reject{ |a,b|
# a: key of the hash entry, b: value of the hash entry (count)
b == 1 # entry that match this condition (occurred only once) is filtered out.
}.keys
# => ["b"]
So, function names like get_duplicated_items should be used instead of function_name to better describe the purpose.
It wants a to be an array, but it doesn't seem to matter what the array is made up of so you'll need some other clue to know what should be in the array.
What the code does is fairly straight foreword. For each item in the array it uses it as a key in a hash. It then basically counts how many times it sees that key. Finally it removes all of the items that only showed up once.
It returns the unique items in the array a that show up 2 or more times.
Related
Code attached below.
arrayy = [[1,'one'],[2,'two'],[3,'three']]
hashy = {}
i = 0
arrayy.each do
hashy[arrayy[i,0]] = arrayy [i,1]
i = i+1
end
puts hashy[1]
puts hashy[2]
puts hashy[3]
end
This code doesn't output anything. No errors. So, I'm guessing that the problem is that nothing is being added to the hash.
Not sure what you are trying to achieve here, but when you are doing arrayy[i,0] in the loop, you are saying that you want to grab zero elements.
When you pass in two numbers as the argument against an array, the first number is the index of the target value and the second number is the length. For example:
arr = ['a', 'b', 'c', 'd', 'e']
puts arr[2, 3]
This would put out ['c','d','e'], which is 'starting from the element with index 2, grab 3 elements'.
You're requesting zero elements, which is an empty array, so you're keying everything on an empty array and all elements collide. To fix that, just use the iterator, as each gives you the elements you need:
arrayy = [[1,'one'],[2,'two'],[3,'three']]
hashy = {}
arrayy.each do |key, value|
hashy[key] = value
end
p hashy
# => {1=>"one", 2=>"two", 3=>"three"}
In your code the actual result you're getting is this:
p hashy
# => {[]=>[[3, "three"]]}
Where here you can see the [] key being used. The p method is really handy for looking at the internal structure of something. p hashy is equivalent to puts hashy.inspect.
As Sergio points out you were probably referencing the arrays the wrong way. To navigate two levels deep you do this:
hashy[arrayy[i][0]] = arrayy [i][1]
Where [i,0] means "at index of the array i select the next 0 elements" whereas [i][0] means "at the array at index i select the value at index 0".
It's worth noting that the simplest solution is to use Array#to_h which already does this:
arrayy = [[1,'one'],[2,'two'],[3,'three']]
hashy = array.to_h
p hashy
# => {1=>"one", 2=>"two", 3=>"three"}
I have a array of hashes like :
arry = {hash1, hash2, hash3,hash4 ....hash_N}
for each hash,
hash1 ={ "deviceId"=>"868403021230682", "osVersion"=>"6.0", "deviceOS"=>"Android",
"appVersion"=>"1.7.2", "action"=>"Enter"}
hash2 = { "deviceId"=>"868403021230682", "osVersion"=>"6.0", "deviceOS"=>"Android",
"appVersion"=>"1.7.2", "action"=>"Leave"}
because it is possible that for each hash "action"=>"Enter" or "Leave" will not always appear as a pair , for example, action for hash3, hash4,hash5 could be all "Enter" . My idea is only consider two hashes who can make a pair like hash1 and hash2, remove other from array or put them into other array.
so the new array should just contain [hash1, hash2, hash7,hash8] , lets say hash7 and 8 are also a pair.
should I use each_with_index? my code is like this:
def get_result(doc)
result = []
doc.each_slice(2).map { |pair|
pair.each_with_index { |element, index|
if ( pair[index].has_value?([:action] => "enter") &&pair[index+1].has_value?([:action] => "Leave")
result.push(pair)
end
}
}
end
but the if statement is not working right , kind of confused about how to use each_with_index hope someone can help me out
Based on the way your method is created , you can do it this way:
def get_result(doc)
doc.each_slice(2).to_a.map{ |ah| ah if ah[0][:action] == 'Enter' && ah[1][:action] == 'Leave'}.compact.flatten
end
Explanation
The variable doc is an array of hashes [hash1, hash2, ...] when we create doc.each_slice(2).to_a will return an array of pair of hashes [[hash1, hash2],[hash3,hash4]...], Now when we do map and get the pair of hashes that has actions per order ('Enter','Leave') we get an array with nil values like this [[hash1,hash2],nil,[hash5,hash6]..] . we use compact to remove the nil values. now the array is like this [[hash1,hash2],[hash5,hash6]..] (array of pair of hashes) and the result expected is an array of hashes, that's why we need flatten, it will remove the inside array and return an array like this [hash1, hash2, hash5, hash6 ...]
If you need to get the list of deleted hashes, i think it would be better if add another method to do it. Otherwise, you can make the get_result method return two arrays.
Here is how you can do it :
def get_result(doc)
removed_elms = []
result = doc.each_slice(2).to_a.map do |ah|
# if you just need them different (not the first one 'Enter' and the second one 'Leave') you need to set the commented condition
# if ['Enter','Leave'].include?(ah[0][:action] && ah[1][:action]) && ah[0][:action] != ah[1][:action]
if ah[0][:action] == 'Enter' && ah[1][:action] == 'Leave'
ah
else
removed_elms << ah
nil
end
end.compact
[result.flatten, removed_elms.flatten]
end
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 7 years ago.
Improve this question
Is there anyway of grouping first common letters in an array of strings?
For example:
array = [ 'hello', 'hello you', 'people', 'finally', 'finland' ]
so when i do
array.group_by{ |string| some_logic_with_string }
The result should be,
{
'hello' => ['hello', 'hello you'],
'people' => ['people'],
'fin' => ['finally', 'finland']
}
NOTE: Some test cases are ambiguous and expectations conflict with other tests, you need to fix them.
I guess plain group_by may not work, a further processing is needed.
I have come up with below code that seems to work for all the given test cases in consistent manner.
I have left notes in the code to explain the logic. Only way to fully understand it will be to inspect value of h and see the flow for a simple test case.
def group_by_common_chars(array)
# We will iteratively group by as many time as there are characters
# in a largest possible key, which is max length of all strings
max_len = array.max_by {|i| i.size}.size
# First group by first character.
h = array.group_by{|i| i[0]}
# Now iterate remaining (max_len - 1) times
(1...max_len).each do |c|
# Let's perform a group by next set of starting characters.
t = h.map do |k,v|
h1 = v.group_by {|i| i[0..c]}
end.reduce(&:merge)
# We need to merge the previously generated hash
# with the hash generated in this iteration. Here things get tricky.
# If previously, we had
# {"a" => ["a"], "ab" => ["ab", "abc"]},
# and now, we have
# {"a"=>["a"], "ab"=>["ab"], "abc"=>["abc"]},
# We need to merge the two hashes such that we have
# {"a"=>["a"], "ab"=>["ab", "abc"], "abc"=>["abc"]}.
# Note that `Hash#merge`'s block is called only for common keys, so, "abc"
# will get merged, we can't do much about it now. We will process
# it later in the loop
h = h.merge(t) do |k, o, n|
if (o.size != n.size)
diff = [o,n].max - [o,n].min
if diff.size == 1 && t.value?(diff)
[o,n].max
else
[o,n].min
end
else
o
end
end
end
# Sort by key length, smallest in the beginning.
h = h.sort {|i,j| i.first.size <=> j.first.size }.to_h
# Get rid of those key-value pairs, where value is single element array
# and that single element is already part of another key-value pair, and
# that value array has more than one element. This step will allow us
# to get rid of key-value like "abc"=>["abc"] in the example discussed
# above.
h = h.tap do |h|
keys = h.keys
keys.each do |k|
v = h[k]
if (v.size == 1 &&
h.key?(v.first) &&
h.values.flatten.count(v.first) > 1) then
h.delete(k)
end
end
end
# Get rid of those keys whose value array consist of only elements that
# already part of some other key. Since, hash is ordered by key's string
# size, this process allows us to get rid of those keys which are smaller
# in length but consists of only elements that are present somewhere else
# with a key of larger length. For example, it lets us to get rid of
# "a"=>["aba", "abb", "aaa", "aab"] from a hash like
# {"a"=>["aba", "abb", "aaa", "aab"], "ab"=>["aba", "abb"], "aa"=>["aaa", "aab"]}
h.tap do |h|
keys = h.keys
keys.each do |k|
values = h[k]
other_values = h.values_at(*(h.keys-[k])).flatten
already_present = values.all? do |v|
other_values.include?(v)
end
h.delete(k) if already_present
end
end
end
Sample Run:
p group_by_common_chars ['hello', 'hello you', 'people', 'finally', 'finland']
#=> {"fin"=>["finally", "finland"], "hello"=>["hello", "hello you"], "people"=>["people"]}
p group_by_common_chars ['a', 'ab', 'abc']
#=> {"a"=>["a"], "ab"=>["ab", "abc"]}
p group_by_common_chars ['aba', 'abb', 'aaa', 'aab']
#=> {"ab"=>["aba", "abb"], "aa"=>["aaa", "aab"]}
p group_by_common_chars ["Why", "haven't", "you", "answered", "the", "above", "questions?", "Please", "do", "so."]
#=> {"a"=>["answered", "above"], "do"=>["do"], "Why"=>["Why"], "you"=>["you"], "so."=>["so."], "the"=>["the"], "Please"=>["Please"], "haven't"=>["haven't"], "questions?"=>["questions?"]}
Not sure, if you can sort by all common letters. But if you want to do sort only by first letter then here it is:
array = [ 'hello', 'hello you', 'people', 'finally', 'finland' ]
result = {}
array.each { |st| result[st[0]] = result.fetch(st[0], []) + [st] }
pp result
{"h"=>["hello", "hello you"], "p"=>["people"], "f"=>["finally", "finland"]}
Now result contains your desired hash.
Hmm, you're trying to do something that's pretty custom. I can think of two classical approaches that sort of do what you want: 1) Stemming and 2) Levenshtein Distance.
With stemming you're finding the root word to a longer word. Here's a gem for it.
Levenshtein is a famous algorithm which calculates the difference between two strings. There is a gem for it that runs pretty fast due to a native C extension.
My Ruby assignment is to iterate through a hash and return the key associated with the lowest value, without using any of the following methods:
#keys #values #min #sort #min_by
I don't understand how to iterate through the hash and store each pair as it comes through, compare it to the last pair that came through, and return the lowest key. This is my code to show you my thought process, but it of course does not work. Any thoughts on how to do this? Thanks!
def key_for_min_value(name_hash)
index = 0
lowest_hash = {}
name_hash.collect do |key, value|
if value[index] < value[index + 1]
lowest = value
index = index + 1
key_for_min_value[value]
return lowest
end
end
end
Track min_value and key_for_min_value. Iterate through the hash, and any time the current value is lower than min_value, update both of these vars. At the end of the loop, return key_for_min_value.
I didn't include sample code because, hey, this is homework. :) Good luck!
One way to do it is transforming our hash into an array;
def key_for_min_value(name_hash)
# Convert hash to array
name_a = name_hash.to_a
# Default key value
d_value= 1000
d_key= 0
# Iterate new array
name_a.each do |i|
# If current value is lower than default, change value&key
if i[1] < d_value
d_value = i[1]
d_key = i[0]
end
end
return d_key
end
You might need to change d_value to something higher or find something more creative :)
We can use Enumerable#reduce method to compare entries and pick the smallest value. Each hash entry gets passed in as an array with 2 elements in reduce method, hence, I am using Array#first and Array#last methods to access key and values.
h = {"a" => 1, "b" => 2, "c" => 0}
p h.reduce{ |f, s| f.last > s.last ? s : f }.first
#=> "c"
Problem:
I need to extract certain keys and count them in a hash, as a sample consider:
data = [{"name"=>"name1", "priority"=>"1", "owner"=>"test3"},
{"name"=>"name1", "priority"=>"1", "owner"=>"test4"},
{"name"=>"name2", "priority"=>"1", "owner"=>"test5"},
{"name"=>"name2", "priority"=>"2", "owner"=>"test5"},
{"name"=>"nae954me2", "priority"=>"2", "owner"=>"test5"}]
I want to count the number of records per each [id (extracted from name) and priority] so that at the end I will have something like:
#{{"priority"=>"1", "id"=>"name1"}=>2, {"priority"=>"1", "id"=>"name2"}=>1, {"priority"=>"2", "id"=>"name2"}=>1}
I'm doing the following but I have a feeling that I'm overcomplicating it:
#!/usr/bin/env ruby
data = [{"name"=>"name1", "priority"=>"1", "owner"=>"test3"},
{"name"=>"name1", "priority"=>"1", "owner"=>"test4"},
{"name"=>"name2", "priority"=>"1", "owner"=>"test5"},
{"name"=>"name2", "priority"=>"2", "owner"=>"test5"},
{"name"=>"nae954me2", "priority"=>"2", "owner"=>"test5"}]
# (1) trash some keys, just because I don't need them
data.each do |d|
d.delete 'owner'
# in the real data I have about 4 or 5 that I'm trashing
d['id'] = d['name'].scan(/[a-z][a-z][a-z][a-z][0-9]/)[0] # only valid ids
d.delete 'name'
end
puts data
#output:
#{"priority"=>"1", "id"=>"name1"}
#{"priority"=>"1", "id"=>"name1"}
#{"priority"=>"1", "id"=>"name2"}
#{"priority"=>"2", "id"=>"name2"}
#{"priority"=>"2", "id"=>nil}
# (2) reject invalid keys
data = data.reject { |d| d['id'].nil? }
puts data
#output:
#{"priority"=>"1", "id"=>"name1"}
#{"priority"=>"1", "id"=>"name1"}
#{"priority"=>"1", "id"=>"name2"}
#{"priority"=>"2", "id"=>"name2"}
# (3) count
counts = Hash.new(0)
data.each do |d|
counts[d] += 1
end
puts counts
#{{"priority"=>"1", "id"=>"name1"}=>2, {"priority"=>"1", "id"=>"name2"}=>1, {"priority"=>"2", "id"=>"name2"}=>1}
any suggestions on improving my method of counting?
There are many ways to do this. (You may have noticed that I've done a lot of editing of my answer, explaining in some detail how a method works, only to realize there's a better way to do it, so out comes the machete.) Here are two solutions. The first was inspired by the approach you took, but I've tried to package it to be more Ruby-like. I'm not sure what constitutes a valid "name", so I've put that determination in a separate method that can be easily changed.
Code
def name_valid?(name)
name[0..3] == "name"
end
data.each_with_object(Hash.new(0)) {|h,g|
(g[{"id"=>h["name"],"priority"=>h["priority"]}]+=1) if name_valid?(h["name"])}
#=> {{"id"=>"name1", "priority"=>"1"}=>2,
# {"id"=>"name2", "priority"=>"1"}=>1,
# {"id"=>"name2", "priority"=>"2"}=>1}
Explanation
Enumerable#each_with_object creates an initially-empty hash with default value zero that is represented by the block variable g. g is built by adding hash elements created from the the elements of data:
g[{"id"=>h["name"],"priority"=>h["priority"]}]+=1
If the hash g has the key
{"id"=>h["name"],"priority"=>h["priority"]}
the value associated with the key is incremented by one. If h does not have this key,
g[{"id"=>h["name"],"priority"=>h["priority"]}]
is set equal to zero before
g[{"id"=>h["name"],"priority"=>h["priority"]}]+=1
is invoked, so the value becomes 1.
Alternative Method
Code
data.each_with_object({}) do |h,g|
hash = { { "id"=>h["name"], "priority"=>h["priority"] } => 1 }
g.update(hash) { |k, vg, _| vg + 1 } if name_valid?(h["name"])
end
#=> {{"id"=>"name1", "priority"=>"1"}=>2,
# {"id"=>"name2", "priority"=>"1"}=>1,
# {"id"=>"name2", "priority"=>"2"}=>1}
Explanation
Here, I've used Hash#update (aka Hash#merge!) to merge each element of data (a hash) into the initially-empty hash h (provided the value of "name" is valid). update's block
{ |k, vg, _| vg + 1 }
is invoked if and only if the merged hash (g) and the merging hash (hash) have the same key, k, in which case the block returns the value of the key. Note the third block variable is the value for the key k for the hash hash. As we do not use that value, I've replaced it with the placeholder _.
Depending on what you mean by "something like" this might do the trick:
data.group_by { |h| [h["name"], h["priority"]] }.map { |k, v| { k => v.size } }
=> [{["name1", "1"]=>2}, {["name2", "1"]=>1}, {["name2", "2"]=>1}, {["nae954me2", "2"]=>1}]