Q:

dynamically update the scale type of chartjs graph

I have a bar chart on a page that has data displayed that is dynamically updated.  The problem I have is that I need to change the type of the y-axis based on the data that is displayed.  I need it to be a linear scale when the data has a range of less than 100 (e.g. 0-99) and logarithmic if it is greater (e.g. 0-1000).
I can calculate whether I need a logarithmic scale or not fine, but when it comes to changing the scale type I am at a loss.  I have tried changing the options object of the chart and calling update():
myChart.options/scales.yAxes.map(function (axis) { axis.type = 'logarithmic' });
myChart.update();

and also tried changing the scale itself directly:
Object.keys(mychart.scales).forEach((function (axisName) {
            var axis = myChart.scales[axisName];
            if (axis && axis.id.startsWith('y')) {
                axis.options.type = 'logarithmic'
            }
        }));
myChart.update();

then I tried using the beforeBuildTicks callback, thinking that changing the type while updating the data and redrawing the graph maybe too late or just at the wrong time:
beforeBuildTicks: function (scale) {
                                if (scale.chart) {
                                    console.log('logarithmic');
                                    scale.options.type = 'logarithmic';
                                 }
                            }

all of these approaches change the chart object, but the display is not affected.
Is there a way to dynamically change the scale type of a chartjs bar chart without destroying and recreating the entire chart?

A:

I finally found a solution to this, but it isn't straightforward.
Thanks to @jordanwillis for the pointer in the right direction in terms of using the `scaleMerge' helper function.
I created a plugin that does all the work:
var changeScaleTypePlugin = {
beforeUpdate: function (chartInstance) {
    var self = this;
    chartInstance.beforeInit = null;
    if (chartInstance.options.changeScale) {
        self.changeScales(chartInstance);
        if (chartInstance.options.scaleTypeChanged) {
            chartInstance.options.scaleTypeChanged = false;
            Object.keys(chartInstance.scales).forEach(function (axisName) {
                var scale = chartInstance.scales[axisName];
                Chart.layoutService.removeBox(chartInstance, scale);
            });
            chartInstance.initialize();
        }
    }
},
changeScales: function (chartInstance) {
    var maxValue = Math.max.apply(null, chartInstance.data.datasets.map(function (dataset) { return Math.max.apply(null, dataset.data); }));
    var minValue = Math.min.apply(null, chartInstance.data.datasets.map(function (dataset) { return Math.min.apply(null, dataset.data); }));
    var logMax = Math.floor(Math.log(maxValue) / Math.LN10);
    var logMin = Math.floor(Math.log(minValue) / Math.LN10);
    if (logMax - logMin > chartInstance.options.maxRankDifference) {
        if (!chartInstance.options.scaleTypeChanged && chartInstance.options.scales.yAxes.filter(function (axis) { return axis.type !== 'logarithmic'; }).length) {
            console.log('logarithmic');
            chartInstance.options.scaleTypeChanged = true;
            chartInstance.options.scales.yAxes = Chart.helpers.scaleMerge(Chart.defaults.scale, { yAxes: chartInstance.options.logarithmicScaleOptions }).yAxes;                
        }
    } else {
        if (!chartInstance.options.scaleTypeChanged && chartInstance.options.scales.yAxes.filter(function (axis) { return axis.type !== 'linear'; }).length) {
            console.log('linear');
            chartInstance.options.scaleTypeChanged = true;
            chartInstance.options.scales.yAxes = Chart.helpers.scaleMerge(Chart.defaults.scale, { yAxes: chartInstance.options.linearScaleOptions }).yAxes;
        }
    }
}

};
Chart.pluginService.register(changeScaleTypePlugin);
and to apply this simply add a few properties to the options of the chart:
options: {
                     linearScaleOptions: [{
                        id: 'y-axis-0',
                         type: 'linear',
                         tick: {
                             // callback: Chart.Ticks.formatters.linear,
                             min: 0
                         }   
                     }],
                     logarithmicScaleOptions: [{
                        id: 'y-axis-0',
                        type: 'logarithmic',
                        ticks: {
                            // callback: function (tickValue, index, ticks)  {
                            //        var remain = tickValue / (Math.pow(10, Math.floor(Math.log(tickValue) / Math.LN10)));

                            //        if (tickValue === 0) {
                            //            return '0';
                            //        } else if (remain === 1 || remain === 2 || remain === 5 || index === 0 || index === ticks.length - 1) {
                            //            return tickValue;
                            //        }
                            //        return '';
                            //},
                            min: 0
                        }
                    }],
                     changeScale: true,
                    maxRankDifference: 1,

the commented out callback on the ticks is for the situation where (like me) you do not want the tick values shown in scientific notation on the log scale.
This gives the output that looks like:

with the max rank property set to 1
or

with it set to 3
or with the callback set:

