JS - group, map and flatten array of objects with arrays in them - arrays

Dears,
I really am not able to achieve following result:
{"2002":[1,2,3...]},
{"2003":[1,2,3,4...]},
...
I have following data (short example below):
{ "Year": "28-01-2020", "numbers": [10, 12, 20, 32, 35, 37] },
{ "Year": "03-10-2019", "numbers": [1, 6, 16, 19, 20, 30] },
{ "Year": "11-01-2018", "numbers": [14, 21, 25, 27, 30, 39] },
{ "Year": "11-08-2015", "numbers": [8, 16, 17, 18, 38, 46] },
I managed to use lodash _.groupBy to achieve following mid result:
[{…}]
0:
2000: Array(4)
0:
Year: "2000"
numbers: (7) [empty, 6, 25, 27, 37, 48, 49]
Year: "2000"
numbers: (7) [empty, 7, 12, 19, 30, 45, 49]
2: {Year: "2000", numbers: Array(7)}
2001: Array(104)
[0 … 99]
[100 … 103]
100: {Year: "2001", numbers: Array(7)}
101: {Year: "2001", numbers: Array(7)}
[0 … 99]
0: {Year: "2002", numbers: Array(7)}
1: {Year: "2002", numbers: Array(7)}
..
but i would like to have one object per year, with all numbers that appeared in sub arrays in this year
Could you please help me, i have tired ES6 map and for in loops, but non give me the proper result
Thank you in advance

This worked for me:
const slicedYear = results.map(elem => (elem = { Year: elem.Year.slice(elem.Year.length - 4), numbers: elem.numbers }));
const groupedResults = _.groupBy(slicedYear, 'Year');
for (let key in groupedResults) {
const selectedNumbers = { [key]: [groupedResults[key].map(elem => elem.numbers)].flat(2) };
}

Related

Mongodb - how to aggregate with "bucket"

that's my dataset:
[
{_id: "628d54495995f1e6589675b3", age: 52, sex: 1, cp: 0, trestbps: 125,
chol: 212, fbs: 0, restecg: "1", thalach: 168, exang: "0", oldpeak: 1},
{_id: "628d54495995f1e6589675b4", age: 53, sex: 1, cp: 1, trestbps: 140,
chol: 203, fbs: 1, restecg: "0", thalach: 155, exang: "1", oldpeak: 3.1},
]
... and so on
I want to group for 2 classes of age (under 40 (included) and over 40 (not included)) and for each class to determine which value of cp is the most frequent.
cp can have four different values: 0, 1, 2, 3
I thought bucket could be the right operator, but I can't get it to work
Thanks for helping

Update values in array of hashes conditionally

results = [
{
:id=>2,
:start=> "3:30",
break: 30,
num_attendees: 14,
hello: {hi: 1}
},
{
id: 3,
start: "3: 40",
break: 40,
num_attendees: {hi: 2},
hello: 4
},
{
id: 4,
start: "4: 40",
break: 10,
num_attendees: 40
}
]
Is there a better way of doing this by avoiding multiple iteration?
results.each do |hash|
hash.each { |k, v|
hash[k] = "" if v.is_a? Hash
}
end
=> [{:id=>2, :start=>"3:30", :break=>30, :num_attendees=>14, :hello=>""}, {:id=>3, :start=>"3: 40", :break=>40, :num_attendees=>"", :hello=>4}, {:id=>4, :start=>"4: 40", :break=>10, :num_attendees=>40}]
Thanks

Python: Doing Calculations on array elements in a list

I have a list of arrays, in which each array represents a cell and the array elements are the coordinates x,y and z, the time point and the cell id. Here a sector of it:
cells=[ ...,
[ 264.847, 121.056, 30.868, 42. , 375. ],
[ 259.24 , 116.875, 29.973, 43. , 375. ],
[ 260.757, 118.574, 32.772, 44. , 375. ]]), array([[ 263.967, 154.089, 55.5 , 38. , 376. ],
[ 260.744, 152.924, 55.5 , 39. , 376. ],
[ 258.456, 151.373, 55.5 , 40. , 376. ],
...,
[ 259.086, 159.564, 48.521, 53. , 376. ],
[ 258.933, 159.796, 48.425, 54. , 376. ],
[ 259.621, 158.719, 51.606, 55. , 376. ]]), array([[ 291.647, 57.582, 28.178, 38. , 377. ],
[ 284.625, 59.221, 30.028, 39. , 377. ],
[ 282.915, 59.37 , 30.402, 40. , 377. ],
...,
[ 271.224, 58.534, 23.166, 42. , 377. ],
[ 270.048, 58.738, 21.749, 43. , 377. ],
[ 268.38 , 58.138, 20.606, 44. , 377. ]]), array([[ 87.83 , 222.144, 26.258, 39. , 378. ],
[ 99.779, 223.631, 24.98 , 40. , 378. ],
[ 104.107, 224.177, 23.728, 41. , 378. ],
...,
[ 127.778, 222.205, 23.123, 63. , 378. ],
[ 126.815, 222.347, 23.934, 64. , 378. ],
[ 127.824, 221.048, 25.508, 65. , 378. ]]),...]
minimumCellCoors = cells
maximumCellCoors = cells
centoEdge = radius+fcr_size
Now i want to change the coordinates x, y and z, so the 0.,1. and 2. element of the arrays in the list to get them in a specific grid. The user gives the spacing for x,y and z and then the operation could look like:
x_Coo=round(x_element/x)*x
y_Coo=round(y_element/y)*y
z_Coo=round(z_element/z)*z
So the real question here is, how could i do a operation on all of the elements in the array ( or in this case the first three elements in the array in the list)?
EDIT
If i use list comprehension to the list like:
[np.round((cellID[:,0]-(centoEdge+1))/x)*x for cellID in minimumCellCoors]
[np.round((cellID[:,1]-(centoEdge+1))/y)*y for cellID in minimumCellCoors]
[np.round((cellID[:,2]-(centoEdge+1))/z)*z for cellID in minimumCellCoors]
[np.round((cellID[:,0]+(centoEdge+1))/x)*x for cellID in maximumCellCoors]
[np.round((cellID[:,1]+(centoEdge+1))/x)*y for cellID in maximumCellCoors]
[np.round((cellID[:,2]+(centoEdge+1))/x)*z for cellID in maximumCellCoors]
How could i fusion the single lists of arrays to one array again?
Best regards!
First off you need to convert your list to a numpy array. It's more proper to create a numpy array instead of a list at first place. Then you can take advantage of numpy's vectorized operation support:
Here is an example:
In [45]: arr = np.arange(100).reshape(4, 5, 5)
In [46]: arr
Out[46]:
array([[[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14],
[15, 16, 17, 18, 19],
[20, 21, 22, 23, 24]],
[[25, 26, 27, 28, 29],
[30, 31, 32, 33, 34],
[35, 36, 37, 38, 39],
[40, 41, 42, 43, 44],
[45, 46, 47, 48, 49]],
[[50, 51, 52, 53, 54],
[55, 56, 57, 58, 59],
[60, 61, 62, 63, 64],
[65, 66, 67, 68, 69],
[70, 71, 72, 73, 74]],
[[75, 76, 77, 78, 79],
[80, 81, 82, 83, 84],
[85, 86, 87, 88, 89],
[90, 91, 92, 93, 94],
[95, 96, 97, 98, 99]]])
In [51]: arr[:,:,:3] = np.round(arr[:,:,:3]/5)*5
In [52]: arr
Out[52]:
array([[[ 0, 0, 0, 3, 4],
[ 5, 5, 5, 8, 9],
[10, 10, 10, 13, 14],
[15, 15, 15, 18, 19],
[20, 20, 20, 23, 24]],
[[25, 25, 25, 28, 29],
[30, 30, 30, 33, 34],
[35, 35, 35, 38, 39],
[40, 40, 40, 43, 44],
[45, 45, 45, 48, 49]],
[[50, 50, 50, 53, 54],
[55, 55, 55, 58, 59],
[60, 60, 60, 63, 64],
[65, 65, 65, 68, 69],
[70, 70, 70, 73, 74]],
[[75, 75, 75, 78, 79],
[80, 80, 80, 83, 84],
[85, 85, 85, 88, 89],
[90, 90, 90, 93, 94],
[95, 95, 95, 98, 99]]])
Note that you can also perform the operations with same length arrays as well as scalars:
For instance you could also do the following:
In [53]: arr[:,:,:3] = np.round(arr[:,:,:3]/5)*[4, 5, 6]

Use mongodb $in operator to search using array of arrays

var foo = [ [ 14, 31, 55, 56, 60, 19 ], [30, 32, 33, 50, 64, 6 ], [9, 15, 22, 35, 48, 3] ];
var bar = await Model.find({
numbers: { $in: foo }
});
console.log(bar);
When I try to run the above code above I get the error below. The model is a mongoose model and the query runs with no problems in a raw mongodb query using robomongo.
{ [CastError: Cast to number failed for value "14,31,55,56,60,19" at path "numbers"]
message: 'Cast to number failed for value "14,31,55,56,60,19" at path "numbers"',
name: 'CastError',
kind: 'number',
value: [ 14, 31, 55, 56, 60, 19 ],
path: 'numbers',
reason: undefined }
You're passing foo, which is an array of arrays to Mongoose with a property which is expecting a Number value, thus Mongoose is complaining to you when it does it's validation by saying that an array is not a number.

Sum an array of hashes in Ruby

I have a ruby array of 3 hashes. Each peace has information about report_data (consumption of 2 types of energy) and monthes_data (same for each). Please see the code below.
arr = [{:report_data=>
[{:type=>
{"id"=>1, "name"=>"electricity"},
:data=>[10, 20, 30, 40]},
{:type=>
{"id"=>2, "name"=>"water"},
:data=>[20, 30, 40, 50]}],
:monthes_data=>
{:monthes=>
["jan", "feb"]},
{:report_data=>
[{:type=>
{"id"=>1, "name"=>"electricity"},
:data=>[15, 25, 35, 45]},
{:type=>
{"id"=>2, "name"=>"water"},
:data=>[25, 35, 45, 55]}],
:monthes_data=>
{:monthes=>
["jan", "feb"]},
{:report_data=>
[{:type=>
{"id"=>1, "name"=>"electricity"},
:data=>[17, 27, 37, 47]},
{:type=>
{"id"=>2, "name"=>"water"},
:data=>[27, 37, 47, 57]}],
:monthes_data=>
{:monthes=>
["jan", "feb"]}]
I'm new to Ruby. Please help me to sum all the data by energy types. In the end I want to have one hash with report_data and monthes_data. I need the result look like:
{:report_data=>
[{:type=>
{:"id"=>1, "name"=>"electricity"},
:data=>[42, 72, 102, 132]},
{:type=>
{"id"=>2, "name"=>"water"}},
:data=>[72, 102, 132, 162]}],
:monthes_data=>
{:monthes=>
["jan", "feb"]}}
arr = [{:report_data=>
[{:type=>
{"id"=>1, "name"=>"electricity"},
:data=>[10, 20, 30, 40]},
{:type=>
{"id"=>2, "name"=>"water"},
:data=>[20, 30, 40, 50]}],
:monthes_data=>
{:monthes=>
["jan", "feb"]}},
{:report_data=>
[{:type=>
{"id"=>1, "name"=>"electricity"},
:data=>[15, 25, 35, 45]},
{:type=>
{"id"=>2, "name"=>"water"},
:data=>[25, 35, 45, 55]}],
:monthes_data=>
{:monthes=>
["jan", "feb"]}},
{:report_data=>
[{:type=>
{"id"=>1, "name"=>"electricity"},
:data=>[17, 27, 37, 47]},
{:type=>
{"id"=>2, "name"=>"water"},
:data=>[27, 37, 47, 57]}],
:monthes_data=>
{:monthes=>
["jan", "feb"]}}]
acc = {}
arr.each do
|e| e[:report_data].each_with_index do
|e, idx|
type = e[:type]['id']
e[:data].each_with_index do
|e, idx|
acc[type] = [] if not acc[type]
acc[type][idx] = (acc[type][idx] or 0) + e
end
end
end
p acc
outputs
{1=>[42, 72, 102, 132], 2=>[72, 102, 132, 162]}
You should be able to reformat this into your record
Code
def convert(arr)
{ :months_data=>arr.first[:months_data],
:report_data=>arr.map { |h| h[:report_data] }.
transpose.
map { |d| { :type=>d.first[:type] }.
merge(:data=>d.map { |g| g[:data] }.transpose.map { |a| a.reduce(:+) }) }
}
end
Example
Half the battle in problems such as this one is visualizing the data. It's much clearer, imo, when written like this:
arr = [
{:report_data=>[
{:type=>{"id"=>1, "name"=>"electricity"}, :data=>[10, 20, 30, 40]},
{:type=>{"id"=>2, "name"=>"water"}, :data=>[20, 30, 40, 50]}
],
:months_data=>{:months=>["jan", "feb"]}
},
{:report_data=>[
{:type=>{"id"=>1, "name"=>"electricity"}, :data=>[15, 25, 35, 45]},
{:type=>{"id"=>2, "name"=>"water"}, :data=>[25, 35, 45, 55]}
],
:months_data=>{:months=>["jan", "feb"]}
},
{:report_data=>[
{:type=>{"id"=>1, "name"=>"electricity"}, :data=>[17, 27, 37, 47]},
{:type=>{"id"=>2, "name"=>"water"}, :data=>[27, 37, 47, 57]}],
:months_data=>{:months=>["jan", "feb"]}
}
]
Let's try it:
convert(arr)
#=> {:months_data=>{:months=>["jan", "feb"]},
# :report_data=>[
# {:type=>{"id"=>1, "name"=>"electricity"}, :data=>[42, 72, 102, 132]},
# {:type=>{"id"=>2, "name"=>"water"}, :data=>[72, 102, 132, 162]}
# ]
# }
Explanation
The first thing I did is concentrate on computing the sums, so I converted this to the values of :report_data. That key, and the key-value pair of months' data, which is the same for all elements (hashes) of arr, can be added back in later.
b = arr.map { |h| h[:report_data] }
#=> [
# [{:type=>{"id"=>1, "name"=>"electricity"}, :data=>[10, 20, 30, 40]},
# {:type=>{"id"=>2, "name"=>"water"}, :data=>[20, 30, 40, 50]}
# ],
# [{:type=>{"id"=>1, "name"=>"electricity"}, :data=>[15, 25, 35, 45]},
# {:type=>{"id"=>2, "name"=>"water"}, :data=>[25, 35, 45, 55]}
# ],
# [{:type=>{"id"=>1, "name"=>"electricity"}, :data=>[17, 27, 37, 47]},
# {:type=>{"id"=>2, "name"=>"water"}, :data=>[27, 37, 47, 57]}
# ]
# ]
If you are not certain that the elements of each array will be sorted by "id", you could write:
b = arr.map { |h| h[:report_data].sort_by { |g| g[:type]["id"] } }
c = b.transpose
#=> [
# [{:type=>{"id"=>1, "name"=>"electricity"}, :data=>[10, 20, 30, 40]},
# {:type=>{"id"=>1, "name"=>"electricity"}, :data=>[15, 25, 35, 45]},
# {:type=>{"id"=>1, "name"=>"electricity"}, :data=>[17, 27, 37, 47]}
# ],
# [{:type=>{"id"=>2, "name"=>"water"}, :data=>[20, 30, 40, 50]},
# {:type=>{"id"=>2, "name"=>"water"}, :data=>[25, 35, 45, 55]},
# {:type=>{"id"=>2, "name"=>"water"}, :data=>[27, 37, 47, 57]}
# ]
# ]
e = c.map {|d| { :type=>d.first[:type] }.
merge(:data=>d.map { |g| g[:data] }.transpose.map { |a| a.reduce(:+) }) }
#=> [{:type=>{"id"=>1, "name"=>"electricity"}, :data=>[42, 72, 102, 132]},
# {:type=>{"id"=>2, "name"=>"water"} , :data=>[72, 102, 132, 162]}]
Lastly, we need to put the put the key :report_data back in and add the months' data:
{ :months_data=>arr.first[:months_data], :report_data=>e }
#=> {:months_data=>{:months=>["jan", "feb"]},
# :report_data=>[
# {:type=>{"id"=>1, "name"=>"electricity"}, :data=>[42, 72, 102, 132]},
# {:type=>{"id"=>2, "name"=>"water"}, :data=>[72, 102, 132, 162]}
# ]
# }
For clarity I've reformatted the input array and removed the :monthes_data key, since that seems to be unrelated to your question. Here's our data:
TL;DR
def zip_sum(arr1, arr2)
return arr2 if arr1.nil?
arr1.zip(arr2).map {|a, b| a + b }
end
def sum_report_data(arr)
arr.flat_map do |item|
item[:report_data].map {|datum| datum.values_at(:type, :data) }
end
.reduce({}) do |sums, (type, data)|
sums.merge(type => data) do |_, old_data, new_data|
zip_sum(old_data, new_data)
end
end
.map {|type, data| { type: type, data: data } }
end
p sum_report_data(arr)
# =>
[ { type: { "id" => 1, "name" => "electricity" }, data: [ 42, 72, 102, 132 ] },
{ type: { "id" => 2, "name" => "water" }, data: [ 72, 102, 132, 162 ] }
]
Explanation
arr = [
{ report_data: [
{ type: { "id" => 1, "name" => "electricity" },
data: [ 10, 20, 30, 40 ]
},
{ type: { "id" => 2, "name" => "water" },
data: [ 20, 30, 40, 50 ]
}
]
},
{ report_data: [
{ type: { "id" => 1, "name" => "electricity" },
data: [ 15, 25, 35, 45 ]
},
{ type: { "id" => 2, "name" => "water" },
data: [ 25, 35, 45, 55 ]
}
]
},
{ report_data: [
{ type: { "id" => 1, "name" => "electricity" },
data: [ 17, 27, 37, 47 ]
},
{ type: { "id" => 2, "name" => "water" },
data: [ 27, 37, 47, 57 ]
}
]
}
]
Step 1
First, let's define a helper method to sum the values of two arrays:
def zip_sum(arr1, arr2)
return arr2 if arr1.nil?
arr1.zip(arr2).map {|a, b| a + b }
end
zip_sum([ 1, 2, 3 ], [ 10, 20, 30 ])
# => [ 11, 22, 33 ]
zip_sum(nil, [ 5, 6, 7 ])
# => [ 5, 6, 7 ]
The way zip_sum works is by "zipping" the two arrays together using Enumerable#zip (e.g. [1, 2].zip([10, 20]) returns [ [1, 10], [2, 20] ]), then adding each pair together.
Step 2
Next, let's use Enumerable#flat_map to get the parts of the data we care about:
result1 = arr.flat_map do |item|
item[:report_data].map {|datum| datum.values_at(:type, :data) }
end
# result1 =>
[ [ { "id" => 1, "name" => "electricity" }, [ 10, 20, 30, 40 ] ],
[ { "id" => 2, "name" => "water" }, [ 20, 30, 40, 50 ] ],
[ { "id" => 1, "name" => "electricity" }, [ 15, 25, 35, 45 ] ],
[ { "id" => 2, "name" => "water" }, [ 25, 35, 45, 55 ] ],
[ { "id" => 1, "name" => "electricity" }, [ 17, 27, 37, 47 ] ],
[ { "id" => 2, "name" => "water" }, [ 27, 37, 47, 57 ] ]
]
Above we've just grabbed the :type and :data values out of each hash the :report_data arrays.
Step 3
Next let's use Enumerable#reduce to iterate over the array of arrays and calculate a running sum of the :data values using the zip_sum method we defined earlier:
result2 = result1.reduce({}) do |sums, (type, data)|
sums.merge(type => data) do |_, old_data, new_data|
zip_sum(old_data, new_data)
end
end
# result2 =>
{ { "id" => 1, "name" => "electricity" } => [ 42, 72, 102, 132 ],
{ "id" => 2, "name" => "water" } => [ 72, 102, 132, 162 ]
}
The result might look a little odd to you because we usually use strings or symbols as hash keys, but in this hash we're using other hashes (the :type values from above) as keys. That's one nice thing about Ruby: You can use any object as a key in a hash.
Inside the reduce block, sums is the hash that's ultimately returned. It starts out as an empty hash ({}, the value we passed to reduce as an argument). type is the hash we're using as a key and data is the array of integers. In each iteration the next values from the result2 array are assigned to type, but sums is updated with whatever value was returned at the end of the block in the previous iteration.
We're using Hash#merge in kind of a tricky way:
sums.merge(type => data) do |_, old_data, new_data|
zip_sum(old_data, new_data)
end
This merges the hash { type => data } (remember that type is the :type hash
and data is the array of integers) into the hash sums. If there are any key collisions, the block will be invoked. Since we only have one key, type, then the block will be invoked if sums[type] already exists. If it does, we call zip_sum with the previous value of sums[type] and data, effectively keeping a running sum of data.
In effect, it's basically doing this:
sums = {}
type, data = result2[0]
sums[type] = zip_sum(sums[type], data)
type, data = result2[1]
sums[type] = zip_sum(sums[type], data)
type, data = result2[3]
# ...and so on.
Step 4
We now have this hash in result3:
{ { "id" => 1, "name" => "electricity" } => [ 42, 72, 102, 132 ],
{ "id" => 2, "name" => "water" } => [ 72, 102, 132, 162 ]
}
That's the data we want, so now we just have to take it out of this weird format and put it into a regular hash with the keys :type and :data:
result3 = result2.map {|type, data| { type: type, data: data } }
# result3 =>
[ { type: { "id" => 1, "name" => "electricity" },
data: [ 42, 72, 102, 132 ]
},
{ type: { "id" => 2, "name" => "water" },
data: [ 72, 102, 132, 162 ]
}
]

Resources