How to calculate the sum of each position of subarrays? - arrays

Now my array is:
[[1,2,3],[4,5,6],[]]
I want to calculate this array and return a result as:
[5,7,9]
If there is a null array, remove it. Then plus every position for each sub array.
If use array's each method, maybe I can get the result. But is there a better way just use ruby's array method?

Another one liner
arr.reject(&:empty?).transpose.map{|x| x.reduce(:+)}
first get rid of the []
reject(&:empty?) # equivalent to reject{|x| x.empty?}
now .transpose to obtain
[[1, 4], [2, 5], [3, 6]]
Add up each sublist with
.map{|x| x.reduce(:+)}

Here's a one-liner that uses a lot of nice Ruby Array methods like reject, reduce, zip, and map.
array.reject(&:empty?).reduce { |result, e| result.zip(e).map { |x,y| x+y } }
See Ruby Array documentation for more details, and to see what other slick things you can do with them.

array = [[1,2,3],[4,5,6],[]]
require 'matrix'
Matrix[*array.reject { |a| a.empty? }].row_vectors.reduce(:+).to_a
#=> [5, 7, 9]

Related

Sort an array of arrays by the number of same occurencies in Ruby

This question is different from this one.
I have an array of arrays of AR items looking something like:
[[1,2,3], [4,5,6], [7,8,9], [7,8,9], [1,2,3], [7,8,9]]
I would like to sort it by number of same occurences of the second array:
[[7,8,9], [1,2,3], [4,5,6]]
My real data are more complexes, looking something like:
raw_data = {}
raw_data[:grapers] = []
suggested_data = {}
suggested_data[:grapers] = []
varietals = []
similar_vintage.varietals.each do |varietal|
# sub_array
varietals << Graper.new(:name => varietal.grape.name, :grape_id => varietal.grape_id, :percent => varietal.percent)
end
raw_data[:grapers] << varietals
So, I want to sort raw_data[:grapers] by the max occurrencies of each varietals array comparing this value: grape_id inside them.
When I need to sort a classical array of data by max occurencies I do that:
grapers_with_frequency = raw_data[:grapers].inject(Hash.new(0)) { |h,v| h[v] += 1; h }
suggested_data[:grapers] << raw_data[:grapers].max_by { |v| grapers_with_frequency[v] }
This code doesn't work cos there are sub arrays there, including AR models that I need to analyze.
Possible solution:
array.group_by(&:itself) # grouping
.sort_by {|k, v| -v.size } # sorting
.map(&:first) # optional step, depends on your real data
#=> [[7, 8, 9], [1, 2, 3], [4, 5, 6]]
I recommend you take a look at the Ruby documentation for the sort_by method. It allows you to sort an array using anything associated with the elements, rather than the values of the elements.
my_array.sort_by { |elem| -my_array.count(elem) }.uniq
=> [[7, 8, 9], [1, 2, 3], [4, 5, 6]]
This example sorts by the count of each element in the original array. This is preceded with a minus so that the elements with the highest count are first. The uniq is to only have one instance of each element in the final result.
You can include anything you like in the sort_by block.
As Ilya has pointed out, having my_array.count(elem) in each iteration will be costlier than using group_by beforehand. This may or may not be an issue for you.
arr = [[1,2,3], [4,5,6], [7,8,9], [7,8,9], [1,2,3], [7,8,9]]
arr.each_with_object(Hash.new(0)) { |a,h| h[a] += 1 }.
sort_by(&:last).
reverse.
map(&:first)
#=> [[7.8.9]. [1,2,3], [4,5,6]]
This uses the form of Hash::new that takes an argument (here 0) that is the hash's default value.

collect all elements and indices of an array in two separate arrays in Ruby

Suppose I have an array array = [1,2,3,4,5]
I want to collect all the elements and indices of the array in 2 separate arrays like
[[1,2,3,4,5], [0,1,2,3,4]]
How do I do this using a single Ruby collect statement?
I am trying to do it using this code
array.each_with_index.collect do |v,k|
# code
end
What should go in the code section to get the desired output?
Or even simpler:
[array, array.each_index.to_a]
I like the first answer that was posted a while ago. Don't know why the guy deleted it.
array.each_with_index.collect { |value, index| [value,index] }.transpose
Actually I am using an custom vector class on which I am calling the each_with_index method.
Here's one simple way:
array = [1,2,3,4,5]
indexes = *array.size.times
p [ array, indexes ]
# => [[1, 2, 3, 4, 5], [0, 1, 2, 3, 4]]
See it on repl.it: https://repl.it/FmWg

Sort a hash of arrays by one of the arrays in ruby

In Ruby, is there a short and sweet way to sort this hash of arrays by score descending:
scored = {:id=>[1, 2, 3], :score=>[8.3, 5, 10]}
so it looks like this?:
scored = {:id=>[3, 1, 2], :score=>[10, 8.3, 5]}
I couldnt find an example where I can sort arrays within a hash like this? I could do this with some nasty code but I feel like there should be a 1 or 2 liner that does it?
You could use sort_by
scored = {:id=>[1, 2, 3], :score=>[8.3, 5, 10]}
scored.tap do |s|
s[:id] = s[:id].sort_by.with_index{ |a, i| -s[:score][i] }
s[:score] = s[:score].sort_by{ |a| -a }
end
#=> {:id=>[3, 1, 2], :score=>[10, 8.3, 5]}
order = scored[:score].each_with_index.sort_by(&:first).map(&:last).reverse
#=> [2,0,1]
scored.update(scored) { |_,a| a.values_at *order }
#=> {:id=>[3, 1, 2], :score=>[10, 8.3, 5]}
If scored is to not to be mutated, replace update with merge.
Some points:
Computing order makes it easy for the reader to understand what's going on.
The second line uses the form of Hash#merge that employs a block to determine the values of keys that are present in both hashes being merged (which here is all keys). This is a convenient way to modify hash values (generally), in part because the new hash is returned.
I sorted then reversed, rather than sorted by negated values, to make the method more rubust. (That is, the elements of the arrays that are the values can be from any class that implements <=>).
With Ruby 2.2+, another way to sort an array arr in descending order is to use Enumerable#max_by: arr.max_by(arr.size).to_a.
The first line could be replaced with:
arr = scored[:score]
order = arr.each_index.sort_by { |i| arr[i] }.reverse
#=> [2,0,1]
Here is one possible solution. It has an intermediate step, where it utilizes a zipped version of the scores object, but produces the correct output:
s = scored.values.inject(&:zip).sort_by(&:last).reverse
#=> [[3, 10], [1, 8.3], [2, 5]]
result = { id: s.map(&:first), score: s.map(&:last) }
#=> { :id => [3, 1, 2], :score => [10, 8.3, 5] }

Recursively count items in a multidimensional array in generic and idiomatic Swift

I have a multi-dimensional array and I need a count of all of the items within all of the arrays, excluding container arrays themselves from the count.
What would be the most generic and idiomatic solution in Swift? I'm guessing it's going to be something functional (a reduce() operation?), but not sure on the best overall approach.
The obvious non-functional approach would be to simply iterate over the array and tally up the number of items.
With the latest Swift 2.0 beta 6 you can use flatten()
let array = [[1, 2, 3], [4, 5], [6]]
array.flatten().count
EDIT: Just tested it: Lazy is not needed, the values are never evaluated, it just calculates endIndex - startIndex of every subcollection.
You can do the following :
let array = [[1, 2, 3], [4, 5], [6]]
let countOfAll = array.map { (nested) -> Int in
return nested.count
}.reduce(0, combine: +) // 6
For Swift 2 you can use flatMap.
var anArray = [[1,0,0], ["asdf","df","lef"], [0,0,1]]
var flatArray = anArray.flatMap { $0 }
print(flatArray.count) // 9

Flatten all values of multiple arrays in Swift

I have a Dictionary of Integer Arrays like below:
let numbers = [1: [2, 3], 4: [5, 6, 7], 8: [9]]
What I really want is a single flattened Array of all of the values (which themselves are arrays), like so:
[2, 3, 5, 6, 7, 9]
Now, I have been able to call numbers.values.array to get:
[[2, 3], [5, 6, 7], [9]]
But what I'm looking for is to merge these one step further, flattening them.
Does Swift (1.1, or 1.2) offer a convenience method for this?
With a combination of numbers.values.array and a reduce function you can simplify this down in one line of code.
numbers.values.array.reduce([], combine: +) // [5,6,7,2,3,9]
However, I would like to note that since you are using a dictionary, you cannot guarantee that the values will be sorted, so you can use the sorted function to accomplish this:
sorted(numbers.values.array.reduce([], combine: +), <) // [2,3,5,6,7,9]
As #Jeffery Thomas stated, you can also use flatmap which was just added in Swift 1.2:
sorted(numbers.values.array.flatMap { $0 }, <)
And to take it a step further, using the global sorted function, the < is extraneous because it is the default and using the global reduce and flatMap functions, you can remove the array property as pointed out by Martin R, so it can be reduced down to:
sorted(reduce(numbers.values, [], +))
sorted(flatMap(numbers.values) { $0 })
Another possible solution is
[].join(numbers.values)
And if you want the values in the order corresponding to the sorted
dictionary keys then it would be
flatMap(sorted(numbers.keys)) { numbers[$0]! }
This is called flattening, and it's a relatively common operation. There are a number of ways to do it, so pick one that suits your needs.
numbers.values.array.reduce([], combine: +) // As stated by #Bluehound
reduce(numbers.values, [], +)
numbers.values.array.flatMap { $0 } // Swift 1.2 (Xcode 6.3)
flatMap(numbers.values) { $0 } // Swift 1.2 (Xcode 6.3)
flatMap may be the most useful, if the next step after flattening is mapping.
NOTE: Thanks #MartinR for the syntax tip.

Resources