I'm trying to use elasticsearch aggregation generate array having sum of each element of array - elasticsearch

I have documents that look like:
{
"arr": [5, 4, 3, 2, 1],
"name": "test"
}
{
"arr": [4, 3, 2, 1, 0],
"name": "test"
}
{
"arr": [1, 0, 0, 0, 0],
"name": "test"
}
I want to use an aggregation (or some other es method) to return:
{
"arr": [10, 7, 5, 3, 1]
}

Elasticsearch doesn't "properly" store lists; in your case internal representation of such field would be arr = 5 AND arr = 4 AND .... Though if all docs have 5 items (or less) you can do something like:
{
"arr": [5, 4, 3, 2, 1],
"arr_0": 5,
"arr_1": 4,
"arr_2": 3,
"arr_3": 2,
"arr_4": 1,
"name": "test"
}
and then 5 sum aggregations over arr_0 ... arr_4.
If there is no limit on the array length, you'll have to compute in your app I'm afraid.

Related

Ruby - Given an array of nested arrays, how to find the max only comparing the last value of each nested array?

Beginner trying to create a simple stock picker program. So far my program takes a flat array of integers representing stock prices (indices are days) and returns an array of nested arrays. Each nested array has three values [each consecutive buy day index, best sell day index, profit]. Like so:
stock_prices = [17, 2, 20, 6, 9, 15, 8, 1, 15, 15]
best_days = [[ 0, 2, 3], [ 1, 2, 18], [ 2, 5, -5],
[ 3, 5, 9], [ 4, 5, 6], [ 5, 8, 0],
[ 6, 8, 7], [ 7, 8, 14], [ 8, 9, 0]]
I would like to find the max profit day, then return an array that contains the index values of the buy and sell days of that day. In this case it would be:
absolute_best = [1, 2]
How do I iterate through an array of nested arrays but only compare the final value of each nested array?
To find the largest array element by a condition (here: the value of its 3rd element) you can use max_by in various ways.
Using array decomposition to extract the 3rd element:
best_days.max_by { |a, b, c| c }
# or
best_days.max_by { |_, _, c| c }
# or
best_days.max_by { |*, c| c }
Using the array as is and retrieve its last value:
best_days.max_by { |ary| ary.last }
# or
best_days.max_by(&:last)
All of the above return [1, 2, 18].
In addition to max_by there's also sort_by which can be used to sort the array by profit.
We can speed up the determination of the desired result as follows. Let me explain the procedure with an example that is slightly modified from the one given in the question. (I changed arr[6] from 8 to 10.)
arr = [17, 2, 20, 6, 9, 15, 10, 1, 15, 15]
days = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
We first consider selling on the last day, day 9. Should we do so, the best buy date is
days.pop
days
#=> [ 0, 1, 2, 3, 4, 5, 6, 7, 8]
days.min_by { |d| arr[d] }
#=> 7 (note arr[7] => 1)
The best pair found so far is given by
[7, 9, 14]
or, expressed as a hash,
{ buy_day: 7, sell_day: 9, net: 14}
We may now eliminate all days d strictly between 7 and 9 for which
arr[d] <= arr[9]
which is here day 8. We are now left with
days = [ 0, 1, 2, 3, 4, 5, 6, 7]
We now consider selling on new last day, day 7. The best we can do is given by
{ buy_day: 1, sell_day: 7, net: -1 }
As -1 < 14, this solution is seen to be sub-optimal. We may now eliminate all days d strictly between 1 and 7 for which
arr[d] <= arr[7]
of which there are none. We next consider
days = [ 0, 1, 2, 3, 4, 5, 6]
and selling on day 6. As the previous best buy date was 1 this obviously will be the best buy date for all sell dates between 2 and 6.
We see that the best solution when selling on day 6 is
{ buy_day: 1, sell_day: 6, net: 8 }
which again is sub-optimal. We may now eliminate all days d strictly between 1 and 6 for which
arr[d] <= arr[6]
of which there are two, days 3 and 4. We therefore next consider
days = [0, 1, 2, 5]
obtaining
{ buy_day: 2, sell_day: 5, net: 13 }
which is found to be sub-optimal (13 < 14). Day 2 cannot be eliminate (since arr[2] > arr[5]) so the new problem becomes
days = [0, 1, 2]
The solution here is
{ buy_day: 0, sell_day: 2, net: 18 }
which is found to be the new optimum. Lastly,
days = [0, 1]
is considered. As
days.pop
days = [0]
days.min_by { |d| arr[d] }
#=> 0
the solution for selling on day 1 is
{ buy_day: 0, sell_day: 1, net: -15 }
which is sub-optimal. The next problem is
days = [0]
Since the array now has only one element we are finished, with the optimal solution being
{ buy_day: 0, sell_day: 2, net: 18 }
We can write a method to implement the above approach to computing an optimal buy-sell pair as follows. Note that I have included a puts statement to illustrate the calculations being made. That statement should of course be removed.
def doit(arr,
days = arr.size.times.to_a,
sell_day = days.pop,
buy_day = days.min_by { |d| arr[d] },
best = { buy_day: nil, sell_day: nil, net: -Float::INFINITY })
puts "days=#{days}, sell_day=#{sell_day}, buy_day=#{buy_day}, best=#{best}"
return best if days.size == 1
sell_price = arr[sell_day]
candidate = sell_price - arr[buy_day]
best = { buy_day: buy_day, sell_day: sell_day, net: candidate } if
candidate > best[:net]
days.reject! { |d| d > buy_day && arr[d] <= sell_price }
sell_day = days.pop
buy_day = days.min_by { |d| arr[d] } if sell_day <= buy_day
doit(arr, days, sell_day, buy_day, best)
end
arr = [17, 2, 20, 6, 9, 15, 10, 1, 15, 15]
doit(arr)
days=[0, 1, 2, 3, 4, 5, 6, 7, 8], sell_day=9, buy_day=7,
best={:buy_day=>nil, :sell_day=>nil, :net=>-Infinity}
days=[0, 1, 2, 3, 4, 5, 6], sell_day=7, buy_day=1,
best={:buy_day=>7, :sell_day=>9, :net=>14}
days=[0, 1, 2, 3, 4, 5], sell_day=6, buy_day=1,
best={:buy_day=>7, :sell_day=>9, :net=>14}
days=[0, 1, 2], sell_day=5, buy_day=1,
best={:buy_day=>7, :sell_day=>9, :net=>14}
days=[0, 1], sell_day=2, buy_day=1,
best={:buy_day=>7, :sell_day=>9, :net=>14}
days=[0], sell_day=1, buy_day=0,
best={:buy_day=>1, :sell_day=>2, :net=>18}
#=> {:buy_day=>1, :sell_day=>2, :net=>18}

How can I filter an array based a hash of arrays while considering each value unique?

In a project of mine, I'm trying to filter newly gathered information that also contains all the data from the previous request. With this filtered data, I'd like to add it to the old data as a new array. New data comes in as an array, and the old data is kept stored in a hash of arrays.
I've tried a number of different methods to remove all past data points from the current data unsuccessfully. An important detail here is that the new data may contain duplicate values that match older ones, but are technically new and should be treated as unique.
Here's an example data set:
x = {
'a' => [],
'b' => [1],
'c' => [],
'd' => [2, 3, 1, 5, 6, 3]
}
y = [0, 2, 3, 5, 1, 5, 6, 3, 1, 10, 7]
z = [0, 5, 10, 7]
x is the old data and y is the new data. The desired output of the filtering would be z that would then be added to x giving us:
x = {
'a' => [],
'b' => [1],
'c' => [],
'd' => [2, 3, 1, 5, 6, 3]
'e' => [0, 5, 10, 7]
}
I would need to continue repeating this for a bit based on some other criteria.
The main hurdle here is getting the filtering done correctly and has been proving difficult for me. Here's a list of some of the things I've tried:
I've tried iterating across the hash's keys and then simply subtracting the arrays, but that doesn't work properly as it gets rid of duplicates too, unfortunately.
irb(main):024:0> d = [2, 3, 1, 5, 6, 3]
=> [2, 3, 1, 5, 6, 3]
irb(main):025:0> y = [0, 2, 3, 5, 1, 5, 6, 3, 1, 10, 7]
=> [0, 2, 3, 5, 1, 5, 6, 3, 1, 10, 7]
irb(main):026:0> y - d
=> [0, 10, 7]
I've tried unions
irb(main):029:0> y | d
=> [0, 2, 3, 5, 1, 6, 10, 7]
and intersections. (which are definitely wrong)
irb(main):030:0> y & d
=> [2, 3, 5, 1, 6]
I tried (unsuccessfully) implementing the following from the second comment here
class Array
def delete_elements_in(ary)
ary.each do |x|
if index = index(x)
delete_at(index)
end
end
end
I've also tried reject!
irb(main):057:0> x = { 'a' => [], 'b' => [1], 'c' => [], 'd' => [2, 3, 1, 5, 6, 3] }
=> {"a"=>[], "b"=>[1], "c"=>[], "d"=>[2, 3, 1, 5, 6, 3]}
irb(main):058:0> y = [0, 2, 3, 5, 1, 5, 6, 3, 1, 10, 7]
=> [0, 2, 3, 5, 1, 5, 6, 3, 1, 10, 7]
irb(main):059:0> x.each_key { |key| y.reject! { |v| a[key].index(v) } }
=> {"a"=>[], "b"=>[1], "c"=>[], "d"=>[2, 3, 1, 5, 6, 3]}
irb(main):060:0> y
=> [0, 10, 7]
A more recent attempt I tried creating a new array from all of x's values and then using that against y, also unsuccessfully. I had just recently thought of trying to keep an array of 'seen' numbers, but I'm still stuck for items that actually need to be removed even though duplicate.
Throughout all this, I've been unable to get [0, 5, 10, 7] as a result.
Halp!
Here's something that might work for you:
>> existing = x.values.flatten
#> [1, 2, 3, 1, 5, 6, 3]
>> z = y.dup # This avoids altering the original `y` array
>> existing.each { |e| z.delete_at(z.index(e)) if z.index(e) }
>> z
#> [0, 5, 10, 7] # z now contains the desired result
>> x['e'] = z
>> pp x
{"a"=>[],
"b"=>[1],
"c"=>[],
"d"=>[2, 3, 1, 5, 6, 3],
"e"=>[0, 5, 10, 7]}
Here's the whole thing in a single method:
def unique_array_filter(hash, new_array)
existing = hash.values.flatten
next_key = hash.keys.max.next
temp = new_array.dup
existing.each { |e| temp.delete_at(temp.index(e)) if temp.index(e) }
hash[next_key] = temp
hash
end
>> unique_array_filter(x, y)
#> {"a"=>[], "b"=>[1], "c"=>[], "d"=>[2, 3, 1, 5, 6, 3], "e"=>[0, 5, 10, 7]}
x.merge(x.keys.max.next => y.difference(x.values.flatten))
#=> {"a"=>[], "b"=>[1], "c"=>[], "d"=>[2, 3, 1, 5, 6, 3], "e"=>[0, 5, 10, 7]}
where Array#difference is defined as follows.
class Array
def difference(other)
h = other.each_with_object(Hash.new(0)) { |e,h| h[e] += 1 }
reject { |e| h[e] > 0 && h[e] -= 1 }
end
end
See the link for an explanation of Array#difference.

Why am I getting a No Method Error?

I'm new to ruby and am still having trouble parsing error messages. My method takes a string in this form:
"8, 2, 1, 6; 3, 1, 3, 2; 6, 4, 2, 7; 7, 3, 2, 4"
My goal is to split each set (offset by ;) into a subarray of an array and convert each value in the subarrays to an integer. Then, I need to print the second value in each subarray. I am getting stuck on the last part. Here is the code:
#input = "8, 2, 1, 6; 3, 1, 3, 2; 6, 4, 2, 7; 7, 3, 2, 4"
#array = []
def splitter
#input.gsub!(/\s+/, '')
#array = #input.split(';').map { |group| group.split(',') }
#array.map! { |subarray| subarray.map! {|v| v.to_i} }
#array.each { |e| print e(1) }
puts ''
end
splitter
and here is the error message:
`block in splitter': undefined method `e' for main:Object (NoMethodError)
I'm wondering if I'm not using the right form for an array of arrays.
str = "8, 2, 1, 6; 3, 1, 3, 2; 6, 4, 2, 7; 7, 3, 2, 4"
str.split(';').map { |e| e.split(',').map(&:to_i) }
#⇒ [[8, 2, 1, 6], [3, 1, 3, 2], [6, 4, 2, 7], [7, 3, 2, 4]]

Comparing numbers in an array, outputting highest number

I need to write a function that takes three phone numbers in an array, adds the digits of each number seperately, and outputs the phone number with be biggest value on the screen.
Numbers are in this form [821-839-1182, 128-389-........]
You could do it like this:
arr = ['821-839-1182', '128-389-4732', '621-411-7324']
arr.max_by { |s| s.each_char.map(&:to_i).reduce(:+) }
#=> "128-389-4732"
We have:
a = arr.map { |s| s.each_char.map(&:to_i) }
#=> [[8, 2, 1, 0, 8, 3, 9, 0, 1, 1, 8, 2],
# [1, 2, 8, 0, 3, 8, 9, 0, 4, 7, 3, 2],
# [6, 2, 1, 0, 4, 1, 1, 0, 7, 3, 2, 4]]
b = a.map { |e| e.reduce(:+) }
#=> [43, 47, 31]
As the largest sum is at index 1, max_by will return the string at that index of arr. Note that '-'.to_i #=> 0.
Replace the '-'s in the phone numbers and do a radix sort to get the highest value.

Sort Array based on other Sorted Array

I have two arrays of the same size and I sort the second one. How can I array the first one to match?
Basic example (imagine replacing Ints with Strings):
var array1 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
var array2 = [5, 2, 3, 4, 5, 6, 8, 5, 4, 5, 1]
array2.sort = ({ $0 > $1})
Result:
array2 is now [8, 6, 5, 5, 5, 5, 4, 4, 3, 2, 1]
How to sort array1's index value to match array2?
array1 should now be [6, 5, 0, 4, 7, 9, 3, 8, 2, 1, 0]
Zip2, sorted and map
array1 = map(sorted(Zip2(array1, array2), {$0.1 > $1.1}), { $0.0 })
Combining filter
var array1 = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0"]
var array2 = [5, 2, 3, 4, 5, 6, 8, 5, 4, 5, 1]
func isEven(x:Int) -> Bool {
return x % 2 == 0
}
let result = map(sorted(filter(Zip2(array1, array2), { isEven($0.1) }), {$0.1 > $1.1}), { $0.0 })
// -> ["6", "5", "3", "8", "1"]
As you can see, the line is too complex, you might want to Array method chain syntax:
let result2 = Array(Zip2(array1, array2))
.filter({ isEven($0.1) })
.sorted({ $0.1 > $1.1 })
.map({ $0.0 })
Anyway, if your array2 is [PFObject], you can implement the function something like:
func isOpen(restaurant: PFObject, forTime time: String, onDay day: Int) -> Bool {
// return `true` if the restaurant is open, `false` otherwise
}

Resources