Is it possible to modify the colModel after data has loaded with jqgrid?

I've got a jqGrid where I need to change the model after the data is loaded, but before it's parsed into the grid. In otherwords, I think I want to do it in the loadComplete handler. I see this approach: Setting JqGrid's colName and colModel from JSON data, but I'm have a bunch of grids already written that use the "load data with jqGrid" approach, rather than the "pre-load data and pass it to jqGrid" one used there, and I'm hoping to avoid re-coding, or making this one different.

(Hiding and showing hidden columns isn't practical, either.)

Is this possible?

More details:

Basically, I don't know what columns I need till I see the data. Say I'm showing traffic by state:

Date      CA     WA     NY    MN
4/20      100    90     85    72
4/21       95    85     89    70

There's only room to show four states, but there might be many more in the data (or there might be fewer), so I want them listed in order of traffic. Right now, the data is coming in like:

{
date : 4-20,
ca : 100,
wa : 90,
ny : 85,
mn : 72
hi : 56,
il : 30
},
{
date : 4-21,
ca : 95,
wa : 85, // note: for a given row, a column might be relatively lower
ny : 89, // than another. The column order is based on the overall
mn : 70
hi : 60,
il : 45
}

or it could be:

{
date : 4-20,
ny : 110,
hi : 95,
il : 90,
wa : 80
}

I've tried setting up columns like state1, state2, state3, state4 and then using jsonmap to remap them, but it didn't work.

loadonce = true, datatype = json


I found one way which seems work OK.

The idea of my solution is following. You use colModel having many hidden columns with the dummy names like 'cm0', 'cm1', 'cm2', ... All the columns has the same data like you need in your case. To fill the data more easy I use column templates existing since jqGrid 3.8.2:

var mygrid=jQuery("#list"),
    cmIntTemplate = {
        width:50,
        sorttype:"int",
        formatter:"integer",
        align:"right",
        hidden:true
    },
    cm = [
        // here we define the first columns which we always has
        // the list can be empty or has some columns with
        // the properties other as the rest (without cmIntTemplate)
        {name:"date",label:"Date",key:true,width:100, fixed:true,
         formatter:'date',formatoptions:{srcformat:"m-d",newformat:"m/d"}}
    ], maxCol = 30, dummyColumnNamePrefix = "cm";

// Add dummy hidden columns. All the columns has the same template
for (i=0;i<maxCol;i++) {
    cm.push({name:dummyColumnNamePrefix+i,template:cmIntTemplate});
}

After that I create jqGrid in the standard way, but with the jsonReader which use the page as function:

jsonReader: {
    repeatitems: false,
    page: function (obj) {
        // ------------------------
        // here I add the main code
        // ------------------------
        return obj.page;
    }
}

The function from the jsonReader.page return the same value like as do default jsonReader, but I use the way with function because the function will be called directly before the reading of the main contain of JSON data. Inside of the code I get the first row of the data and use it's property names to fill jsonmap property of the corresponding column and set the column name. Additionally I make some dummy columns needed to display all the JSON data visible and the rest dummy column hidden. The last thing which should be done is correction of the grid width which was calculated previously. So the grid will look like this:

enter image description here

or like this

enter image description here

depend on the JSON input data.

The code of the page function is following:

page: function (obj) {
    var rows = obj.rows, colModel = mygrid[0].p.colModel,
        cmi, iFirstDummy, firstRow, prop,
        orgShrinkToFit, isFound,
        showColNames = [], hideColNames = [];

    if (typeof(rows) === "undefined" || !$.isArray(rows) || rows.length === 0) {
        // something wrong need return
        return obj.page;
    }

    // find the index of the first dummy column
    // in the colModel. If we use rownumbers:true,
    // multiselect:true or subGrid:true additional
    // columns will be inserted at the begining
    // of the colModel
    iFirstDummy = -1;
    for(i=0;i<colModel.length;i++) {
        cmi = colModel[i];
        if (dummyTestRegex.test(cmi.name)) {
            iFirstDummy = i;
            break;
        }
    }
    if (iFirstDummy === -1) {
        // something wrong need return
        return obj.page;
    }

    orgShrinkToFit = clearShrinkToFit();

    // we get the first row of the JSON data
    firstRow = rows[0];
    for (prop in firstRow) {
        if (firstRow.hasOwnProperty(prop)) {
            // we will use the properties name of the first row
            // as the names of the column headers of the grid

            // find column index having prop as the name
            isFound = false;
            for(i=0;i<colModel.length;i++) {
                cmi = colModel[i];
                if (cmi.name === prop) {
                    isFound = true;
                    showColNames.push(prop);
                    break;
                }
            }
            if(!isFound) {
                // labels defines the column names
                cmi = colModel[iFirstDummy];
                showColNames.push(cmi.name);
                mygrid.jqGrid('setLabel',cmi.name,prop);

                // because of bug in jqGrid with calculation of width
                // we have to reset the width
                cmi.width = cmIntTemplate.width;

                // we set jsonmap which jqGrid will use instead
                // of dummy column names to read all row data
                cmi.jsonmap = prop;
                iFirstDummy++;
            }
        }
    }

    // fill the list of unused columns
    for(i=0;i<colModel.length;i++) {
        cmi = colModel[i];
        if ($.inArray(cmi.name, showColNames) === -1 && dummyTestRegex.test(cmi.name)) {
            hideColNames.push(cmi.name);
        }
    }
    mygrid.jqGrid('showCol',showColNames);
    mygrid.jqGrid('hideCol',hideColNames);

    setGridWidthAndRestoreShrinkToFit(orgShrinkToFit);

    return obj.page;
}

Inside of the page function I use small helper functions

var clearShrinkToFit = function() {
        // save the original value of shrinkToFit
        var orgShrinkToFit = mygrid.jqGrid('getGridParam','shrinkToFit');
        // set shrinkToFit:false to prevent shrinking
        // the grid columns after its showing or hiding
        mygrid.jqGrid('setGridParam',{shrinkToFit:false});
        return orgShrinkToFit;
    },
    setGridWidthAndRestoreShrinkToFit = function(orgShrinkToFit) {
        // calculate the new grid width
        var width=0, i=0, headers=mygrid[0].grid.headers, l=headers.length;
        for (;i<l; i++) {
            var th = headers[i].el;
            if (th.style.display !== "none") {
                width += $(th).outerWidth();
            }
        }

        // restore the original value of shrinkToFit
        mygrid.jqGrid('setGridParam',{shrinkToFit:orgShrinkToFit});

        // set the grid width
        mygrid.jqGrid('setGridWidth',width);
    },
    dummyTestRegex = new RegExp(dummyColumnNamePrefix+"(\\d)+");

The working demo you can see here.

UPDATED: Another answer with the demo shows how to create the grid which has another format of input data: [[], [], ...] (array of arrays) - matrix.