Ruby, unique hashes in array based on multiple fields - ruby

I'd like to get back an array of hashes based on sport and type combination
I've got the following array:
[
{ sport: "football", type: 11, other_key: 5 },
{ sport: "football", type: 12, othey_key: 100 },
{ sport: "football", type: 11, othey_key: 700 },
{ sport: "basketball", type: 11, othey_key: 200 },
{ sport: "basketball", type: 11, othey_key: 500 }
]
I'd like to get back:
[
{ sport: "football", type: 11, other_key: 5 },
{ sport: "football", type: 12, othey_key: 100 },
{ sport: "basketball", type: 11, othey_key: 200 },
]
I tried to use (pseudocode):
[{}, {}, {}].uniq { |m| m.sport and m.type }
I know I can create such array with loops, I'm quite new to ruby and I'm curious if there's a better (more elegant) way to do it.

Try using Array#values_at to generate an array to uniq by.
sports.uniq{ |s| s.values_at(:sport, :type) }

One solution is to build some sort of key with the sport and type, like so:
arr.uniq{ |m| "#{m[:sport]}-#{m[:type]}" }
The way uniq works is that it uses the return value of the block to compare elements.

require 'pp'
data = [
{ sport: "football", type: 11, other_key: 5 },
{ sport: "football", type: 12, othey_key: 100 },
{ sport: "football", type: 11, othey_key: 700 },
{ sport: "basketball", type: 11, othey_key: 200 },
{ sport: "basketball", type: 11, othey_key: 500 }
]
results = data.uniq do |hash|
[hash[:sport], hash[:type]]
end
pp results
--output:--
[{:sport=>"football", :type=>11, :other_key=>5},
{:sport=>"football", :type=>12, :othey_key=>100},
{:sport=>"basketball", :type=>11, :othey_key=>200}]

Related

How to concatenate differents values for one keys

I have a trouble for several days and I need help to solve it.
I have an hash with multiple values for the same key :
{"answers":
[
{"id":1,"value":true},
{"id":3,"value":false},
{"id":2,"value":3},
{"id":1,"value":false},
{"id":2,"value":false},
{"id":2,"value":1}
]
}
I want a method to group all the values for one key, as an exemple :
{
"answers": [
{
"id":1, "value": [true, false]
},
{
"id":3, "value": [false]
},
{
"id":2, "value":[3, false, 1]
}
]
}
I've tried with the reduce method, but I cant find a way to link values to keys.
Anyone can help me with that ?
Thanks!
It looks like you want Enumerable#group_by to regroup the array of hashes by the :id key in each hash.
This method takes the answers array and returns a new, transformed answers array:
def transform_answers(answers)
answers
.group_by { |h| h[:id] }
.each_value { |a| a.map! { |h| h[:value] } }
.map { |id, value| { id: id, value: value } }
end
You can use it like this:
hash = {
answers: [
{ id: 1, value: true },
{ id: 1, value: false },
{ id: 2, value: 3 },
{ id: 2, value: false },
{ id: 2, value: 1 },
{ id: 3, value: false }
]
}
transformed_answers = transform_answers(hash[:answers]) # => [{:id=>1, :value=>[true, false]}, {:id=>2, :value=>[3, false, 1]}, {:id=>3, :value=>[false]}]
You can easily take the transformed answers and put them back into a hash resembling the original input:
transformed_hash = { answers: transformed_answers }
hash = {
answers: [
{ id: 1, value: true },
{ id: 1, value: false },
{ id: 2, value: 3 },
{ id: 2, value: false },
{ id: 2, value: 1 },
{ id: 3, value: false }
]
}
def doit(answers)
answers.each_with_object({}) do |g,h|
h.update(g[:id]=>{ id: g[:id], value: [g[:value]] }) do |_,o,n|
{ id: o[:id], value: o[:value]+n[:value] }
end
end.values
end
{ answers: doit(hash[:answers]) }
#=> {:answers=>[
# {:id=>1, :value=>[true, false]},
# {:id=>2, :value=>[3, false, 1]},
# {:id=>3, :value=>[false]}
# ]
# }
This uses the form of Hash#update (aka merge!) that employs a block to determine the values of keys that are present in both hashes being merged. That block is
do |_k,o,n|
{ id: o[:id], value: o[:value]+n[:value] }
end
See the doc for update for definitions of the three block variables, _k, o and n. I've written the first block variable (the common key) _k, rather than k, to signify that it is not used in the block calculation.
Note that before values is executed in doit the method has constructed the following hash.
{1=>{:id=>1, :value=>[true, false]},
2=>{:id=>2, :value=>[3, false, 1]},
3=>{:id=>3, :value=>[false]}}

AmCharts serial multiple data sets

Taking a look at different examples on the documentation, I found that the only way to include multiple graphs (or lines) on a serial chart would be to do so by placing the data in one array as such:
[{
category: 1,
value1: .8,
value2: .64,
},
{
category: 2,
value1: .75,
value2: -.4,
}];
However, this is rather tedious if you have multiple data sets you are trying to display at once; is there an alternative way to do this, where you would pass multiple arrays at once (this is what I figured an implementation would look like, but it is not the case):
[
// First set of data
{ category: 0, value: .5},
{ category: 1, value: .5},
{ category: 2, value: .5},
{ category: 3, value: .5},
{ category: 4, value: .3},
{ category: 5, value: 1}
],
// Second set of data
[
{ category: 0, value: .5 },
{ category: 1, value: .3 },
{ category: 2, value: .25 },
{ category: 3, value: .6 },
{ category: 4, value: .79 },
{ category: 5, value: .81 }
]],
Any ideas on how this may be done? Or would I need to do switch to a different type of chart?
This isn't possible in the regular AmCharts JavaScript Charts library. The only supported format is a single array of objects with the values consolidated by category as you've noticed. You'll have to preprocess your data beforehand.
The AmCharts Stock Chart library supports separate arrays of data in the dataSets array, however it only supports date-based data.
AmCharts.makeChart("chartdiv", {
"type": "stock",
"dataSets": [{
// other properties omitted
"dataProvider": [{
category: "2017-08-01,
value: 3
}, {
category: "2017-08-02,
value: 2
}, {
category: "2017-08-03,
value: 1
}, // ...
]
}, {
// other properties omitted
"dataProvider": [{
category: "2017-08-01,
value: 10
}, {
category: "2017-08-02,
value: 9
}, {
category: "2017-08-03,
value: 5
}, // ...
]
},
// ...
]
// ...
});
You can see this in action in the any of the stock chart demos.

search in multidimensional array if value is true or false

I have a multidimensional array looks like as below
{
patient: {
value: 66,
entry_date: "2017-01-17",
source: "self entered",
risk_level: nil,
risk_ranges: [{
level: "low",
is_active: false,
range_low: 80,
range_high: 139,
unit: "mg/dL"
}, {
level: "moderate",
is_active: false,
range_low: 140,
range_high: 199,
unit: "mg/dL"
}, {
level: "high",
is_active: false,
range_low: 200,
range_high: nil,
unit: "mg/dL"
}]
}
}
I want to find out if is_active is false in the risk_ranges array.
Of course I can access to patient.risk_ranges[0].is_active, patient.risk_ranges[1].is_active and patient.risk_ranges[2].is_active but wondering if there's a better way to write this.
if the is_active is false in all 3 objects in array then set patient.risk_level = high
How can I do that?
You can do something along the lines of
if patient.risk_ranges.all?{ |range| range.is_active == false}
patient.risk_level = "high"
end

How to create a nested model in kendo-mvvm

Consider the following JSON values:
[
{
OrderId: 1,
OrderName: 'order 1'
OrderItems: [
{
ProductId: 1,
ProductName: "sample name"
},
{
ProductId: 2,
ProductName: "sample name 2"
}
}
}
]
I am defining a model with this structure:
var model = kendo.data.Model.define({
id: "OrderId",
fields: {
OrderId: {
type: "number",
editable: false
},
OrderName: {
type: "string",
editable: false
},
OrderItems: {
??????????????
}
}
});
Is it possible to define a model in such a way that we can change the OrderItems during the CRUD operation?
It looks like you're mixing column definitions for your controls and the model for you orders. This simplified model will help you get started with the array of order items.
var model = kendo.data.Model.define(
{
"Order": {
"OrderId": 12345,
"OrderName": "myname",
"OrderItems": [{
"ProductId": 1,
"ProductName": "sample name"
}, {
"ProductId": 2,
"ProductName": "another product"
}]
}
}
)
You can iterate through the model.Order.OrderItems collection with $.each

kendo Treelist keeps telling no records to Display

I´m intending to use some kendo controls from telerik and I started playing with the Treelist control. I´m doing this with Visual Studio 2013 VB or C#.
The plan is to create a webservice that sends some (serialized) data and the user has to manually press a button which is linked to a $Ajax request that POSTS for the data. That data should be passed to the treelist.
But whatever I try it keeps telling me: No Records to Display
Questions:
1 Why is the sample I provided not working. I copied almost literally one of the demo´s.
2 Do you need a seprate datasource or can you put the data direct in the treelist as well?
Thanks in advance.
Rick (NL)
Sample:
`<script type="text/javascript">
$(document).ready(function () {
var dataSource = new kendo.data.TreeListDataSource({
data: [
{ "Item": "Item0", "id": 0, "ParentId": null },
{ "Item": "Item1", "id": 1, "ParentId": 0 },
{ "Item": "Item2", "id": 2, "ParentId": 1 },
{ "Item": "Item3", "id": 3, "ParentId": 1 },
{ "Item": "Item4", "id": 4, "ParentId": null },
{ "Item": "Item5", "id": 5, "ParentId": null },
{ "Item": "Item6", "id": 6, "ParentId": 5 },
{ "Item": "Item7", "id": 7, "ParentId": 5 },
{ "Item": "Item8", "id": 8, "ParentId": 7 },
{ "Item": "Item9", "id": 9, "ParentId": 7 }
],
schema: {
model: {
id: "id",
expanded: true
}
}
});
$("#treelist").kendoTreeList({
dataSource: dataSource,
height: 540,
columns: [
{ field: "Item" },
{ field: "id" },
{ field: "ParentId" }
]
});
});
</script>
parentId also needs to be null if it's the top-level record. That really tripped me up.
#user4659712 yes, you can define the schema. parentId can be anything as long as you tell through the schema:
vm.treeListDataSource = new kendo.data.TreeListDataSource({
data: organizations,
schema: {
model: {
id: "Id",
fields: {
Id: { type: "number", nullable: false },
parentId: { field: "OverliggendeId", nullable: true }
},
expanded: true
}
},
pageSize: 20
});
This is down to a simple typo the "ParentId" needs to be parentId (note lowercase p).
see this dojo for a working version.
http://dojo.telerik.com/uWaSO
I've fallen foul of this before.
Lets not forget the 'parentId' field on the model definition. We don't have to change the JSON properties that way (ParentId needs describing in fields as well though):
var dataSource = new kendo.data.TreeListDataSource({
data: [
{ Id: 1, Name: "test", ParentId: null },
{ Id: 2, Name: "test 2", ParentId: 1 }
],
schema: {
model: {
id: "Id",
parentId: "ParentId",
fields: {
Name: { field: "Name", type: "string" },
StoreCount: { field: "StoreCount", type: "number" },
ParentId: { field: "ParentId", nullable: true }
},
expanded: true
}
}
});
Does anyone know if you can specify a database column with a different name to be the parentid? For example, my table has node_id and parent_node_id.

Resources