How do I keep the hash associations when building permutations - ruby

UPDATE: Possible solution at the end, but certainly not performant or ideal.
I created the following method that gives me close to what I want.
def multi_permutations(collection)
case collection.length
when 1
return collection.shift[1]
when 0
raise "You must pass in a multidimensional collection."
end
a = collection.shift[1]
b = multi_permutations(collection)
return_value = []
a.each do |a_value|
b.each do |b_value|
return_value << [a_value] + [b_value]
end
end
return return_value
end
When I pass in a hash with nested arrays that looks like this...
my_collection["item_9"] = [152]
my_collection["item_2"] = [139, 143, 145]
my_collection["item_13"] = [138, 142, 150]
my_collection["item_72"] = [137, 149, 151, 154]
my_collection["item_125"] = [140, 141]
my_collection["item_10"] = [144, 146, 147, 148, 153]
I want it to create an array of hashes with all permutations that looks like this...
[{ "item_9": 152 }, { "item_2": 139 }, { "item_13": 138 }, { "item_72": 137 }, { "item_125": 140 }, { "item_10": 144 }]
[{ "item_9": 152 }, { "item_2": 139 }, { "item_13": 138 }, { "item_72": 137 }, { "item_125": 140 }, { "item_10": 146 }]
[{ "item_9": 152 }, { "item_2": 139 }, { "item_13": 138 }, { "item_72": 137 }, { "item_125": 140 }, { "item_10": 147 }]
[{ "item_9": 152 }, { "item_2": 139 }, { "item_13": 138 }, { "item_72": 137 }, { "item_125": 140 }, { "item_10": 148 }]
[{ "item_9": 152 }, { "item_2": 139 }, { "item_13": 138 }, { "item_72": 137 }, { "item_125": 140 }, { "item_10": 153 }]
.
.
.
[{ "item_9": 152 }, { "item_2": 145 }, { "item_13": 150 }, { "item_72": 154 }, { "item_125": 141 }, { "item_10": 144 }]
[{ "item_9": 152 }, { "item_2": 145 }, { "item_13": 150 }, { "item_72": 154 }, { "item_125": 141 }, { "item_10": 146 }]
[{ "item_9": 152 }, { "item_2": 145 }, { "item_13": 150 }, { "item_72": 154 }, { "item_125": 141 }, { "item_10": 147 }]
[{ "item_9": 152 }, { "item_2": 145 }, { "item_13": 150 }, { "item_72": 154 }, { "item_125": 141 }, { "item_10": 148 }]
[{ "item_9": 152 }, { "item_2": 145 }, { "item_13": 150 }, { "item_72": 154 }, { "item_125": 141 }, { "item_10": 153 }]
What this function ends up doing is close but I lose the relationships.
[152, [139, [138, [137, [140, 144]]]]]
[152, [139, [138, [137, [140, 146]]]]]
[152, [139, [138, [137, [140, 147]]]]]
[152, [139, [138, [137, [140, 148]]]]]
[152, [139, [138, [137, [140, 153]]]]]
.
.
.
[152, [145, [150, [154, [141, 144]]]]]
[152, [145, [150, [154, [141, 146]]]]]
[152, [145, [150, [154, [141, 147]]]]]
[152, [145, [150, [154, [141, 148]]]]]
[152, [145, [150, [154, [141, 153]]]]]
The relationships are very important to me. The reason is, I plan to hydrate an object where the attrs are the keys in the hash. I'm sure this can be done in a better way, and I'm open to suggestions.
So one possible solution I've come up with is to create a keys array then flatten the permutations and zip them together in a hash.
results = []
permutations = multi_permutations(possibilities)
permutations.each do |permutation|
results << Hash[keys.zip permutation.flatten!]
end
This ends up giving me...
{"item_9"=>152, "item_2"=>145, "item_13"=>150, "item_72"=>154, "item_125"=>141, "item_10"=>146}
{"item_9"=>152, "item_2"=>145, "item_13"=>150, "item_72"=>154, "item_125"=>141, "item_10"=>147}
{"item_9"=>152, "item_2"=>145, "item_13"=>150, "item_72"=>154, "item_125"=>141, "item_10"=>148}
{"item_9"=>152, "item_2"=>145, "item_13"=>150, "item_72"=>154, "item_125"=>141, "item_10"=>153}

I couldn't get your solution to run because you did not provide how to call it, so I couldn't compare the efficiency.
What do you think about this solution? (the single argument has to be a Hash of Arrays, just like your my_collection)
my_collection = {}
my_collection["item_9"] = [152]
my_collection["item_2"] = [139, 143, 145]
my_collection["item_13"] = [138, 142, 150]
my_collection["item_72"] = [137, 149, 151, 154]
my_collection["item_125"] = [140, 141]
my_collection["item_10"] = [144, 146, 147, 148, 153]
def permutations!(input)
input.each do |key, possibilities|
possibilities.map!{|p| {key => p} }
end
digits = input.keys.map!{|key| input[key] }
digits.shift.product(*digits)
end
results = permutations!(my_collection)
Please notice that this method modifies the input object because of the map! usage.

Related

Form a JSON request using previous response value in JMETER

On my request I am getting the following response,
{
"studyDTO": {
"studyId": 191,
"studyCode": "test_ispptest2"
},
"sites": [],
"subjects": [],
"visits": [],
"sftpLocations": [],
"dicomLocations": [],
"fileSystemLocations": [],
"rawFileSystemLocations": [],
"states": null,
"modalities": [
{
"studyId": 191,
"submitValue": "ct",
"displayValue": "Conventional CT",
"orderOfDisplay": 30
},
{
"studyId": 191,
"submitValue": "multi_slice_spiral_ct",
"displayValue": "Multi-Slice Spiral CT",
"orderOfDisplay": 50
},
{
"studyId": 191,
"submitValue": "dxa",
"displayValue": "DXA",
"orderOfDisplay": 60
},
{
"studyId": 191,
"submitValue": "mri",
"displayValue": "MRI",
"orderOfDisplay": 100
},
{
"studyId": 191,
"submitValue": "unknown",
"displayValue": "Unknown",
"orderOfDisplay": 240
}
],
"examDates": [],
"series": null,
"transferType": null,
"customFolder": false,
"customFile": false,
"folderStructure": null,
"fileStructure": null,
"allSites": true,
"allSubjects": true,
"allVisits": true,
"allStates": false,
"allExamDates": true,
"allModalities": false,
"allSeries": false,
"softEditOverride": false,
"includePS": false,
"includeSR": false,
"includeRTStruct": false,
"dicomTemplate": null,
"errorMessage": null,
"successMessage": null
}
in the response I received 5 modality value this can be more and in my next request body in the JSON type I want to add all the modalities, how I can do this using JSR223 Post-Processer,
Request Sample:
{
"studyDTO": {
"studyId": 191,
"studyCode": "test_ispptest2"
},
"allVisits": true,
"modalities": [
{
"studyId": 191,
"submitValue": "ct",
"displayValue": "Conventional CT",
"orderOfDisplay": 30
},
{
"studyId": 191,
"submitValue": "multi_slice_spiral_ct",
"displayValue": "Multi-Slice Spiral CT",
"orderOfDisplay": 50
},
{
"studyId": 191,
"submitValue": "dxa",
"displayValue": "DXA",
"orderOfDisplay": 60
},
{
"studyId": 191,
"submitValue": "mri",
"displayValue": "MRI",
"orderOfDisplay": 100
},
{
"studyId": 191,
"submitValue": "unknown",
"displayValue": "Unknown",
"orderOfDisplay": 240
}
],
"includePS": null
}
I have developed so far, but don't have a clue to form the request JSON
import groovy.json.JsonSlurper
def jsonString = prev.getResponseDataAsString();
def jsonConvert = new JsonSlurper();
def object = jsonConvert.parseText(jsonString);
def modalityS = object.modalities.size().toString();
def modalitySize = modalityS?.isInteger() ? modalityS.toInteger() : null
for (int i = 0; i < modalitySize ; i++) {
def modalityOrderOfDisplay = object.modalities[i].orderOfDisplay;
def modalSubmitValue = object.modalities[i].submitValue;
def modalDisplayValue = object.modalities[i].displayValue;
log.info('----------------------->'+modalityOrderOfDisplay);
log.info('----------------------->'+modalSubmitValue);
log.info('----------------------->'+modalDisplayValue);
}
Take a look at JsonBuilder class
Something like:
def response = new groovy.json.JsonSlurper().parse(prev.getResponseData())
def request = [:]
request.put('studyDTO', response.studyDTO)
request.put('allVisits', response.allVisits)
request.put('modalities', response.modalities)
request.put('includePS', null)
vars.put('request', new groovy.json.JsonBuilder(request).toPrettyString())
should do the trick for you.
You will be able to refer the generated request body as ${request} where required.
More information:
Apache Groovy - Parsing and producing JSON
Apache Groovy: What Is Groovy Used For?

Loop in hash for ruby

I have:
blockchain = [
{ from_user: nil, to_user: "brian", amount: 21000 },
{ from_user: "brian", to_user: "ben", amount: 9000 },
{ from_user: "brian", to_user: "jeff", amount: 7000 },
{ from_user: "ben", to_user: "jeff", amount: 400 },
{ from_user: "brian", to_user: "jeff", amount: 1500 },
{ from_user: "jeff", to_user: "brian", amount: 4500 },
{ from_user: "jeff", to_user: "ben", amount: 1750 }
]
I want to get the final amounts for each person, total but coin remaining. It should print out:
Brian's balance is 8000
Ben's balance is 10350
Jeff's balance is 2650
I am trying to figure out how to write the code. Could someone help?
You can do it in the following way :
blockchain = [
{ from_user: nil, to_user: "brian", amount: 21000 },
{ from_user: "brian", to_user: "ben", amount: 9000 },
{ from_user: "brian", to_user: "jeff", amount: 7000 },
{ from_user: "ben", to_user: "jeff", amount: 400 },
{ from_user: "brian", to_user: "jeff", amount: 1500 },
{ from_user: "jeff", to_user: "brian", amount: 4500 },
{ from_user: "jeff", to_user: "ben", amount: 1750 }
]
users = {}
blockchain.each do |block|
users[block[:from_user]] = 0 if !users.keys.include?(block[:from_user]) && block[:from_user].present?
users[block[:to_user]] = 0 if !users.keys.include?(block[:to_user])
users[block[:to_user]] = users[block[:to_user]] + block[:amount]
users[block[:from_user]] = users[block[:from_user]] - block[:amount] if block[:from_user].present?
end
puts users
The users hash will contain your required output
{"brian"=>8000, "ben"=>10350, "jeff"=>2650}
Below is simple implementation to achieve balance for each person,
add = blockchain.group_by { |x| x[:to_user] }.reject { |k,v| k.nil? }.transform_values { |v| v.map { |x| x[:amount] }.sum }
# => {"brian"=>25500, "ben"=>10750, "jeff"=>8900}
sub = blockchain.group_by { |x| x[:from_user] }.reject { |k,v| k.nil? }.transform_values { |v| v.map { |x| x[:amount] }.sum }
# => {"brian"=>17500, "ben"=>400, "jeff"=>6250}
people = (add.keys + sub.keys).uniq
# => ["brian", "ben", "jeff"]
people.each { |x| puts "#{x.capitalize}'s balance is #{add[x].to_i - sub[x].to_i}" }
# Brian's balance is 8000
# Ben's balance is 10350
# Jeff's balance is 2650
# => ["brian", "ben", "jeff"]
Step 1: Create an empty hash with a default value of zero
The keys of this hash will be the users and whose values will be the amounts each user has at any given time.
For a hash h to have a default value of 0 means that h[k] returns 0 if h does not have a key k. Here we will be writing h[k] += 1, which expands to
h[k] = h[k] + 1
If (before this expression is executed) h does not have a key k h[k] on the right returns zero, so we have
h[k] = 0 + 1
#=> 1
Thereafter, h[k] on the right will be a positive integer.
If you are wondering why h[k] on the left of the expression does not return 0, recall that h[k] = h[k] + 1 is syntactic sugar for the actual expression
h.[]=(h.[](k) + 1)
We have the method []= on the left and the method [] on the right. It is h.[](k) that returns the default value when h does not have a key k.
Find a Hash method m (not actually "m") to do that.
h = Hash.m(?)
The question mark means that you must supply an argument for the method m.
Step 2: Step through the hashes in blockchain to update the hash h
blockchain.each do |g|
h[?] += ?
h[?] -= ? unless ? == nil
end
h #=> {"brian"=>8000, "ben"=>10350, "jeff"=>2650}
We now have the information needed to print the desired results.
In practice we would chain these two steps using the method Enumerable#each_with_object:
blockchain.each_with_object(Hash.m(?)) do |g,h|
h[?] += ??
h[?] -= ?? unless ?? == nil
end
#=> {"brian"=>8000, "ben"=>10350, "jeff"=>2650}
Here ? and ?? are respectively placeholders for an argument and an expression.
Awesome answer by Amogh Hegde, but somehow It was not working on my system with ruby 2.5.1 but if you add require 'active_support/all' on the top script, it will work. But I tried to do it without including anything. So have done very minor change. Following is the complete code I just changed if condition to check :from_user is present or not, so I have converted to a string and then checked it with the empty method.
blockchain = [
{ from_user: nil, to_user: "brian", amount: 21000 },
{ from_user: "brian", to_user: "ben", amount: 9000 },
{ from_user: "brian", to_user: "jeff", amount: 7000 },
{ from_user: "ben", to_user: "jeff", amount: 400 },
{ from_user: "brian", to_user: "jeff", amount: 1500 },
{ from_user: "jeff", to_user: "brian", amount: 4500 },
{ from_user: "jeff", to_user: "ben", amount: 1750 }
]
users = {}
blockchain.each do |block|
users[block[:from_user]] = 0 if !users.keys.include?(block[:from_user]) && !block[:from_user].to_s.empty?
users[block[:to_user]] = 0 if !users.keys.include?(block[:to_user])
users[block[:to_user]] = users[block[:to_user]] + block[:amount]
users[block[:from_user]] = (users[block[:from_user]] - block[:amount]) if !block[:from_user].to_s.empty?
end
puts users
I am getting following response.
{"brian"=>8000, "ben"=>10350, "jeff"=>2650}
In one iteration:
blockchain.each_with_object(Hash.new(0)) do |h, nh|
nh[h[:from_user]] -= h[:amount]
nh[h[:to_user]] += h[:amount]
end
#=> {nil=>-21000, "brian"=>8000, "ben"=>10350, "jeff"=>2650}
To keep track of the full movements:
blockchain.each_with_object(Hash.new { |h,k| h[k] = Hash.new(0) }) do |h, nh|
nh[h[:from_user]][:out] -= h[:amount]
nh[h[:to_user]][:in] += h[:amount]
end
#=> {nil=>{:out=>-21000}, "brian"=>{:in=>25500, :out=>-17500}, "ben"=>{:in=>10750, :out=>-400}, "jeff"=>{:in=>8900, :out=>-6250}}
Other option: could calculate incomes and outcomes using Enumerable#group_by and Hash#transform_values, then merge together.
Incomes are:
incomes = blockchain.group_by { |k,v| k[:to_user] }.transform_values{ |a| a.sum { |h| h[:amount] }}
#=> {"brian"=>25500, "ben"=>10750, "jeff"=>8900}
Outcomes are (negatives):
outcomes = blockchain.group_by { |k,v| k[:from_user] }.transform_values{ |a| -a.sum { |h| h[:amount]}}
#=> {nil=>-21000, "brian"=>-17500, "ben"=>-400, "jeff"=>-6250}
Then merge passing a block using Hash#merge:
incomes.merge(outcomes) { |k, income, outcome| income + outcome }
#=> {"brian"=>8000, "ben"=>10350, "jeff"=>2650, nil=>-21000}

AmCharts - Controlling value axis labels

I want my y-axis to have labels 0%, 25%, 50%, 75%, 100%
I would have expected a gridCount of 4 or 5 to do it, but it refuses. I've tried labelFrequency set to 25 but that doesn't work either.
window.AmCharts.makeChart('chartdiv', {
'type': 'serial',
'categoryField': 'category',
'dataDateFormat': 'YYYY-MM-DD',
'startDuration': 1,
'theme': 'light',
'categoryAxis': {
'parseDates': true,
'axisThickness': 0,
'color': '#989898',
'gridThickness': 0
},
'graphs': [
{
'fillAlphas': 1,
'type': 'column',
'valueField': 'column-1'
}
],
'valueAxes': [
{
'zeroGridAlpha': -2,
'titleColor': '#989898',
'axisThickness': 0,
'color': '#989898',
'gridThickness': 1,
unit: '%',
autoGridCount: false,
minimum:0,
maximum:100,
gridCount: 5
}
],
'dataProvider': [
{
'category': '2014-03-01',
'column-1': 8
},
{
'category': '2014-03-02',
'column-1': 16
},
{
'category': '2014-03-03',
'column-1': 2
},
{
'category': '2014-03-04',
'column-1': 7
},
{
'category': '2014-03-05',
'column-1': 5
},
{
'category': '2014-03-06',
'column-1': 9
},
{
'category': '2014-03-07',
'column-1': 4
},
{
'category': '2014-03-08',
'column-1': 15
},
{
'category': '2014-03-09',
'column-1': 12
},
{
'category': '2014-03-10',
'column-1': 17
},
{
'category': '2014-03-11',
'column-1': 18
},
{
'category': '2014-03-12',
'column-1': 21
},
{
'category': '2014-03-13',
'column-1': 24
},
{
'category': '2014-03-14',
'column-1': 23
},
{
'category': '2014-03-15',
'column-1': 24
}
]
})
Unfortunately there isn't a way to outright set your own axis divisions through the value axis properties. To workaround this, you can disable the value axis labels and grids and set up your own grid and labels using guides:
'valueAxes': [{
'zeroGridAlpha': -2,
'titleColor': '#989898',
'axisThickness': 0,
'color': '#989898',
'gridThickness': 1,
minimum: 0,
maximum: 100,
gridAlpha: 0,
tickLength: 0,
labelsEnabled: false,
guides: [{
value: 0,
label: "0%",
tickLength: 5,
lineAlpha: .15
}, {
value: 25,
label: "25%",
tickLength: 5,
lineAlpha: .15
}, {
value: 50,
label: "50%",
tickLength: 5,
lineAlpha: .15
}, {
value: 75,
label: "75%",
tickLength: 5,
lineAlpha: .15
}, {
value: 100,
label: "100%",
tickLength: 5,
lineAlpha: .15
},
]
}],
Demo below:
AmCharts.makeChart('chartdiv', {
'type': 'serial',
'categoryField': 'category',
'dataDateFormat': 'YYYY-MM-DD',
'startDuration': 1,
'theme': 'light',
'categoryAxis': {
'parseDates': true,
'axisThickness': 0,
'color': '#989898',
'gridThickness': 0
},
'graphs': [{
'fillAlphas': 1,
'type': 'column',
'valueField': 'column-1'
}],
'valueAxes': [{
'zeroGridAlpha': -2,
'titleColor': '#989898',
'axisThickness': 0,
'color': '#989898',
'gridThickness': 1,
minimum: 0,
maximum: 100,
gridAlpha: 0,
tickLength: 0,
labelsEnabled: false,
guides: [{
value: 0,
label: "0%",
tickLength: 5,
lineAlpha: .15
}, {
value: 25,
label: "25%",
tickLength: 5,
lineAlpha: .15
}, {
value: 50,
label: "50%",
tickLength: 5,
lineAlpha: .15
}, {
value: 75,
label: "75%",
tickLength: 5,
lineAlpha: .15
}, {
value: 100,
label: "100%",
tickLength: 5,
lineAlpha: .15
},
]
}],
'dataProvider': [{
'category': '2014-03-01',
'column-1': 8
},
{
'category': '2014-03-02',
'column-1': 16
},
{
'category': '2014-03-03',
'column-1': 2
},
{
'category': '2014-03-04',
'column-1': 7
},
{
'category': '2014-03-05',
'column-1': 5
},
{
'category': '2014-03-06',
'column-1': 9
},
{
'category': '2014-03-07',
'column-1': 4
},
{
'category': '2014-03-08',
'column-1': 15
},
{
'category': '2014-03-09',
'column-1': 12
},
{
'category': '2014-03-10',
'column-1': 17
},
{
'category': '2014-03-11',
'column-1': 18
},
{
'category': '2014-03-12',
'column-1': 21
},
{
'category': '2014-03-13',
'column-1': 24
},
{
'category': '2014-03-14',
'column-1': 23
},
{
'category': '2014-03-15',
'column-1': 24
}
]
})
<script src="//www.amcharts.com/lib/3/amcharts.js"></script>
<script src="//www.amcharts.com/lib/3/serial.js"></script>
<div id="chartdiv" style="width: 100%; height: 400px;"></div>

AmCharts: custom button to hide/show graph

I would like to have my own buttons to hide/show lines on a linear graph.
The legend is fine, but I want my own HTML/CSS.
Is there a way to do this?
Attaching the hide/show event maybe?
Thank you
You can call the showGraph and hideGraph methods from your buttons' events. Since they take the graph instance, you'll want to have access to the chart to pass in the desired graph instance either by accessing the graphs array directly or calling getGraphById if you set ids for your graphs, then check the graph's hidden property to know when to call showGraph or hideGraph
Assuming you have the graph index in your button's markup like <button data-graph-index="0">Toggle first graph</button>, you could do something like this:
button.addEventListener('click', function(e) {
var graph = chart.graphs[e.currentTarget.dataset.graphIndex];
if (graph.hidden) {
chart.showGraph(graph);
}
else {
chart.hideGraph(graph);
}
});
Here's a demo:
var chart;
Array.prototype.forEach.call(
document.querySelectorAll('.toggle-graph'),
function (button) {
button.addEventListener('click', function(e) {
var graph = chart.graphs[e.currentTarget.dataset.graphIndex];
if (graph.hidden) {
chart.showGraph(graph);
}
else {
chart.hideGraph(graph);
}
});
}
);
chart = AmCharts.makeChart("chartdiv", {
"type": "serial",
"theme": "light",
"dataProvider": [{
"year": 1994,
"cars": 1587,
"motorcycles": 650,
"bicycles": 121
}, {
"year": 1995,
"cars": 1567,
"motorcycles": 683,
"bicycles": 146
}, {
"year": 1996,
"cars": 1617,
"motorcycles": 691,
"bicycles": 138
}, {
"year": 1997,
"cars": 1630,
"motorcycles": 642,
"bicycles": 127
}, {
"year": 1998,
"cars": 1660,
"motorcycles": 699,
"bicycles": 105
}, {
"year": 1999,
"cars": 1683,
"motorcycles": 721,
"bicycles": 109
}, {
"year": 2000,
"cars": 1691,
"motorcycles": 737,
"bicycles": 112
}, {
"year": 2001,
"cars": 1298,
"motorcycles": 680,
"bicycles": 101
}, {
"year": 2002,
"cars": 1275,
"motorcycles": 664,
"bicycles": 97
}, {
"year": 2003,
"cars": 1246,
"motorcycles": 648,
"bicycles": 93
}, {
"year": 2004,
"cars": 1318,
"motorcycles": 697,
"bicycles": 111
}, {
"year": 2005,
"cars": 1213,
"motorcycles": 633,
"bicycles": 87
}, {
"year": 2006,
"cars": 1199,
"motorcycles": 621,
"bicycles": 79
}, {
"year": 2007,
"cars": 1110,
"motorcycles": 210,
"bicycles": 81
}, {
"year": 2008,
"cars": 1165,
"motorcycles": 232,
"bicycles": 75
}, {
"year": 2009,
"cars": 1145,
"motorcycles": 219,
"bicycles": 88
}, {
"year": 2010,
"cars": 1163,
"motorcycles": 201,
"bicycles": 82
}, {
"year": 2011,
"cars": 1180,
"motorcycles": 285,
"bicycles": 87
}, {
"year": 2012,
"cars": 1159,
"motorcycles": 277,
"bicycles": 71
}],
"valueAxes": [{
"gridAlpha": 0.07,
"position": "left",
"title": "Traffic incidents"
}],
"graphs": [{
"title": "Cars",
"valueField": "cars"
}, {
"title": "Motorcycles",
"valueField": "motorcycles"
}, {
"title": "Bicycles",
"valueField": "bicycles"
}],
"chartCursor": {
"cursorAlpha": 0
},
"categoryField": "year",
"categoryAxis": {
"startOnAxis": true,
"axisColor": "#DADADA",
"gridAlpha": 0.07,
"title": "Year"
},
"export": {
"enabled": true
}
});
<script type="text/javascript" src="//www.amcharts.com/lib/3/amcharts.js"></script>
<script type="text/javascript" src="//www.amcharts.com/lib/3/serial.js"></script>
<button class="toggle-graph" data-graph-index="0">Toggle first graph</button>
<button class="toggle-graph" data-graph-index="1">Toggle second graph</button>
<button class="toggle-graph" data-graph-index="1">Toggle third graph</button>
<div id="chartdiv" style="width: 100%; height: 300px;"></div>
Not sure if its a version issue or a different way I was using the api, but the accepted answer hadn't worked for me. In fact chart.graphs itself wasn't defined.
All I was used was
chart.show()
and
chart.hide()
Background...
const [chart, setChart] = useState(null)
...
let newChart = root.container.children.push(
am5xy.XYChart.new(root, {
panX: false,
panY: false,
wheelX: 'none',
wheelY: 'none',
layout: root.verticalLayout,
})
setChart(newChart)
I also had some tooltips and modal dialogue (to show when the chart is empty)
const hideChartTooltip = () => {
const tooltip = chart.series.getIndex(0).get('tooltip')
if (tooltip) {
tooltip.set('forceHidden', true)
tooltip.hide()
}
}
useEffect(() => {
if (!chart) {
console.log('show/hide graph no-op')
return
}
console.log(`chart view mode : ${isChartView}`)
if (isChartView) {
chart.show()
}
else {
console.log('hiding the graph... ')
hideChartTooltip()
chart.hide()
}
}, [isChartView])
Ref: Sprite - hide
Inheritance hierarchy...
Chart <- Container <- Sprite

Converting Array of Array of Hashes to Array of Hashes

I have an array of arrays of hashes.
items =
[{ "item_9": 152 }, { "item_2": 139 }, { "item_13": 138 }, { "item_72": 137 }, { "item_125": 140 }, { "item_10": 144 }]
[{ "item_9": 152 }, { "item_2": 139 }, { "item_13": 138 }, { "item_72": 137 }, { "item_125": 140 }, { "item_10": 146 }]
[{ "item_9": 152 }, { "item_2": 139 }, { "item_13": 138 }, { "item_72": 137 }, { "item_125": 140 }, { "item_10": 147 }]
[{ "item_9": 152 }, { "item_2": 139 }, { "item_13": 138 }, { "item_72": 137 }, { "item_125": 140 }, { "item_10": 148 }]
[{ "item_9": 152 }, { "item_2": 139 }, { "item_13": 138 }, { "item_72": 137 }, { "item_125": 140 }, { "item_10": 153 }]
.
.
.
[{ "item_9": 152 }, { "item_2": 145 }, { "item_13": 150 }, { "item_72": 154 }, { "item_125": 141 }, { "item_10": 144 }]
[{ "item_9": 152 }, { "item_2": 145 }, { "item_13": 150 }, { "item_72": 154 }, { "item_125": 141 }, { "item_10": 146 }]
[{ "item_9": 152 }, { "item_2": 145 }, { "item_13": 150 }, { "item_72": 154 }, { "item_125": 141 }, { "item_10": 147 }]
[{ "item_9": 152 }, { "item_2": 145 }, { "item_13": 150 }, { "item_72": 154 }, { "item_125": 141 }, { "item_10": 148 }]
[{ "item_9": 152 }, { "item_2": 145 }, { "item_13": 150 }, { "item_72": 154 }, { "item_125": 141 }, { "item_10": 153 }]
What I'd like to do is change it to be an array of hashes...
items =
{"item_9"=>152, "item_2"=>145, "item_13"=>150, "item_72"=>154, "item_125"=>141, "item_10"=>146}
{"item_9"=>152, "item_2"=>145, "item_13"=>150, "item_72"=>154, "item_125"=>141, "item_10"=>147}
{"item_9"=>152, "item_2"=>145, "item_13"=>150, "item_72"=>154, "item_125"=>141, "item_10"=>148}
{"item_9"=>152, "item_2"=>145, "item_13"=>150, "item_72"=>154, "item_125"=>141, "item_10"=>153}
I believe I can do this using...
items.map! { |item| item.reduce({}, :merge) }
However, it's not very performant. At least it's not performant enough when you have 140million records. Is there a better way to do this?
perhaps a bit longer but it works much quicker:
require 'benchmark'
items = [
[{ item_9: 152 }, { item_2: 139 }, { item_13: 138 }, { item_72: 137 }, { item_125: 140 }, { item_10: 146 }],
[{ item_9: 152 }, { item_2: 139 }, { item_13: 138 }, { item_72: 137 }, { item_125: 140 }, { item_10: 147 }],
[{ item_9: 152 }, { item_2: 139 }, { item_13: 138 }, { item_72: 137 }, { item_125: 140 }, { item_10: 148 }],
[{ item_9: 152 }, { item_2: 139 }, { item_13: 138 }, { item_72: 137 }, { item_125: 140 }, { item_10: 153 }],
[{ item_9: 152 }, { item_2: 145 }, { item_13: 150 }, { item_72: 154 }, { item_125: 141 }, { item_10: 144 }],
[{ item_9: 152 }, { item_2: 145 }, { item_13: 150 }, { item_72: 154 }, { item_125: 141 }, { item_10: 146 }],
[{ item_9: 152 }, { item_2: 145 }, { item_13: 150 }, { item_72: 154 }, { item_125: 141 }, { item_10: 147 }],
]
n = 100_000
Benchmark.bm do |b|
b.report do
n.times do |i|
items.map { |item| item.reduce({}, :merge) }
end
end
b.report do
n.times do |i|
# the winer
items.map { |item| item.reduce({}, :update) }
end
end
b.report do
n.times do |i|
items.map { |i| i.inject({}) { |f,c| f.update c } }
end
end
end
as #tokland suggests, item.reduce({}, :update) is even faster:
user system total real
6.300000 0.080000 6.380000 ( 6.386180)
1.840000 0.020000 1.860000 ( 1.860073)
2.220000 0.020000 2.240000 ( 2.237294)
thanks #tokland
Since performance is an issue, it might be time for for loops yields as well as noticing interesting facts about your data, if any. For example, your data seem to have many repetitive items. Is that a rule or just a coincidence?
If you’re sure you have a two-level array (no other arrays inside the pairs) and exactly two items in each pair, then it’s faster and shorter to use this:
array = [['A', 'a'], ['B', 'b'], ['C', 'c']]
hash = Hash[*array.flatten]
For more than two-level deep arrays this will give the wrong result or even an error (for some inputs).
array = [['A', 'a'], ['B', 'b'], ['C', ['a', 'b', 'c']]]
hash = Hash[*array.flatten]
# => {"A"=>"a", "B"=>"b", "C"=>"a", "b"=>"c"}
But if you’re running Ruby 1.8.7 or greater you can pass an argument to Array#flatten and have it flatten only one level deep:
# on Ruby 1.8.7+
hash = Hash[*array.flatten(1)]
# => {"A"=>"a", "B"=>"b", "C"=>["a", "b", "c"]}

Resources