Is something like derived_virtual_indices for datatable available in ag grid?

I am converting from datatable to ag grid. I am using derived_virtual_indices in datatable. Is there an equivalent in ag_grid?

Hello @Brent,

I believe that you are looking for virtualRowData.

I already found that. What derived_virtual_indices gives me is the indices in the full row data which made it easier for me to do what I needed to do. I suppose I could get it myself from virtualRowData but datatable already retrieved it for me.

There is nothing that is just the index in the order of how the rows are displayed.

Okay, no problem. I will just have to rework what I need to do. I just wanted to make sure the equivalent was not available.

Thanks

To create it would be easy though:

derived_virtual_indices = [i['rowIndex'] for i in virtualRowData]

Actually, thats not available in the data. XD

Thanks, that is what I had in mind.

Check out here to see what’s available.

Yes, rowIndex is not available in the data. I was thinking of the cellValueChanged data that has rowIndex.

You could pass the index of the df to the rowData and then reference it similar to how I stated above.

I see the example in the documentation https://dash.plotly.com/dash-ag-grid/row-ids
It seems like if I put the rowId in the grid that I would have to get rid of the column every time I converted to a dataframe or to write the data to the database. That would be a pain. The datatable derived_virtual_indices are nice because it gets me the indexes without cluttering the row data.

From reading the documentation, the grid has the indices available but there does not seem to be a way to get access to them other than explicitly putting them in the data.

Check this out:

import dash_ag_grid as dag
from dash import Dash, html, Input, Output
import pandas as pd
import json

app = Dash(__name__)

df = pd.read_csv(
    "https://raw.githubusercontent.com/plotly/datasets/master/ag-grid/olympic-winners.csv"
)

def addIndex(df):
    df['rowIndex'] = df.index
    return df

def stripIndexDF(data):
    df = pd.DataFrame(data)
    if 'rowIndex' in df.columns:
        df = df.drop(columns=['rowIndex'])
    return df

columnDefs = [
    {"field": "country"},
    {"field": "year"},
    {"field": "athlete"},
    {"field": "age"},
    {"field": "sport"},
    {"field": "total"},
]

app.layout = html.Div(
    [
        dag.AgGrid(
            id="grid",
            rowData=addIndex(df).to_dict("records")[:20],
            columnDefs=columnDefs,
            defaultColDef={"resizable": True, "sortable": True, "filter": True},
            columnSize="sizeToFit",
        ),
        html.Div(id='indexOrder'),
        html.Div(id='rowData')
    ],
)

@app.callback(
    Output('indexOrder', 'children'),
    Input('grid', 'virtualRowData')
)
def showIndexOrder(data):
    if data:
        return json.dumps([x['rowIndex'] for x in data])

@app.callback(
    Output('rowData', 'children'),
    Input('grid', 'virtualRowData')
)
def showStrippedData(data):
    if data:
        return json.dumps(stripIndexDF(data).to_dict('records'))

if __name__ == "__main__":
    app.run(debug=True)

We didnt want to add all of the things that DataTable had because there were already a lot of props.

Considering you always need to convert your data into a pandas dataframe, just use a function to convert the data instead. :slight_smile:

Okay, so maybe not that big of a pain. I will see if doing something like this is easier than modifying the rest of my code.

My use case is that I have a grid that allows filtering and selecting. I have a button for delete filtered and delete selected as well as apply filtered and apply selected. This allows the user to do all of the filtering and selecting they want and then click one of the buttons. For the delete filtered for example all I had to do was make a dataframe from the data and delete the rows by the derived_virtual_indices in reverse order. Update my database with the new data. Then output the updated row data to the grid.

So now, I have to do something like you did above or I have to do a lot more work to match up the filtered data to the original data to know what to delete in my database and also remove from the grid. I actually do not delete from the database only mark a delete column as True. This allows undeleting.

Just for kicks, couldnt you use the filterModel in reverse and use an update string?

Or… have a uid for each row and say:

uids = "'" + "','".join([i['uid'] for i in data]) + "'"

f"update table set deleted = 1 where uid not in ({uids})"
f"update table set deleted = 0 where uid in ({uids})"

This is actually something I wouldnt recommend if there are multiple people using the same data though… as they would be stepping on toes, etc. :stuck_out_tongue:

I will have to think through it. Another big gotcha I have found is that selected_rows in datatable returns row indices but SelectedRows in ag grid returns the data for the selected rows. I have to do something about that too. In datatable I had indices for both selected and filtered. In ag grid I do not have indices for either. :frowning_face:

It is looking like something like your addindex above might be the easiest way forward.

On a good note. cellValueChanged makes handling cell changes much easier. :smiley:

Especially since it has the row index.

For data, I always prefer unique identifiers, especially inside of databases. XD

The grid can handle multiple keys, just like joining multiple tables. I interact with DBs daily via a grid.

And yes, cellValueChanged is how I build a changelog and push changes back to the db upon save to different tables using the aforementioned getRowId. :wink:

Yep, I also build a changelog. It is much easier with cellValueChanged.

1 Like

And by the way I was wondering why ag grid uses camelCase when the rest of dash uses snake_case. Python normally uses snake_case. Java normally uses camelCase. I suppose the people working on ag grid must be more Java oriented than Python. :crazy_face:

JavaScript, yes.

AG Grid is all JS based, to match the underlying props, it makes more sense to follow along with their properties. That way, you can also read their docs and come up with cool things.