
# SPAN

Basic column grouping can be implemented with [column layout](/guides/column/layout).
However, in cases where the ending boundaries between each column are different, as shown in the demo below, a slightly different method is required.

The implementation method is to find the least common multiple between each column group and divide the obtained lowest common multiple according to the number of columns.
In the example below, the number of columns in the first column group is 2, and the number of columns in the second column group is 3.
The least common multiple of 2 and 3 is 6.
Therefore, the number of columns in the first column group is 2, so each column in the first group has 3 cellSpans.

```js
RealGrid.setLocale('en');

const fields = [
  { fieldName: 'OrderID', dataType: 'text' },
  { fieldName: 'CustomerID', dataType: 'text' },
  { fieldName: 'EmployeeID', dataType: 'text' },
  { fieldName: 'OrderDate', dataType: 'datetime', datetimeFormat: 'yyyy-MM-dd', amText: 'AM', pmText: 'PM' },
  { fieldName: 'CompanyName', dataType: 'text' },
  { fieldName: 'Country', dataType: 'text' },
  { fieldName: 'Phone', dataType: 'text' },
  { fieldName: 'ProductName', dataType: 'text' },
  { fieldName: 'QuantityPerUnit', dataType: 'text' },
  { fieldName: 'Quantity', dataType: 'number' },
  { fieldName: 'UnitPrice', dataType: 'number' }
];

const columns = [
  { name: 'Country', fieldName: 'Country', type: 'data', width: '100', header: { text: 'Country' } },
  { name: 'CompanyName', fieldName: 'CompanyName', type: 'data', width: '120', header: { text: 'CompanyName' } },
  { name: 'OrderID', fieldName: 'OrderID', type: 'data', width: '80', header: { text: 'Order ID' } },
  { name: 'CustomerID', fieldName: 'CustomerID', type: 'data', width: '90', header: { text: 'Customer ID' } },
  { name: 'EmployeeID', fieldName: 'EmployeeID', type: 'data', width: '90', header: { text: 'Employee ID' } },
  { name: 'OrderDate', fieldName: 'OrderDate', type: 'data', width: '130', header: { text: 'Order Date' } },
  { name: 'Phone', fieldName: 'Phone', type: 'data', width: '100', header: { text: 'Phone' } },
  { name: 'ProductName', fieldName: 'ProductName', type: 'data', width: '200', header: { text: 'Product Name' } },
  { name: 'QuantityPerUnit', fieldName: 'QuantityPerUnit', type: 'data', width: '100', header: { text: 'Quantity / Unit' }, numberFormat: '#,##0' },
  { name: 'Quantity', fieldName: 'Quantity', type: 'data', width: '100', header: { text: 'Quantity' }, numberFormat: '#,##0' },
  { name: 'UnitPrice', fieldName: 'UnitPrice', type: 'data', width: '100', header: { text: 'Unit Price' }, numberFormat: '#,##0' }
];

function getMultiLevelColumns(colNames) {
  function leastCommonMultiple(arr) {
    function gcd(a, b) {
      while (b != 0) {
        var temp = a % b;
        a = b;
        b = temp;
      }
      return a;
    }

    function lcm(a, b) {
      return a * b / gcd(a, b);
    }

    var result = lcm(arr[0], arr[1]);
    for (var i = 2; i < arr.length; i++) {
      result = lcm(result, arr[i]);
    }
    return result;
  }

  var arrLen = colNames.map(function (x) { return x.length });
  var lcmValue = leastCommonMultiple(arrLen);

  var parentGrpColObj = {};
  parentGrpColObj.direction = 'vertical';
  parentGrpColObj.items = [];
  parentGrpColObj.header = { visible: false };

  for (var i = 0; i < colNames.length; i++) {
    var grpColObj = {};
    grpColObj.direction = 'horizontal';
    grpColObj.items = [];
    grpColObj.header = { visible: false };

    for (var j = 0; j < colNames[i].length; j++) {
      var cellSpan = lcmValue / colNames[i].length;
      var column = {};

      column.column = colNames[i][j];
      column.cellSpan = cellSpan;
      column.width = 50;

      grpColObj.items.push(column);
      for (var k = 0; k < cellSpan - 1; k++) {
        grpColObj.items.push(50);
      }
    }
    parentGrpColObj.items.push(grpColObj);
  }

  return [parentGrpColObj];
}

const { gridView, dataProvider } = RealGrid.createGrid('realgrid', {
  columns,
  header: {
    height: -1
  },
  display: {
    rowHeight: 36,
    emptyMessage: 'There is no data to display.'
  },
  footer: {
    height: 40
  },
  stateBar: {
    width: 16
  },
  onInitialized: (grid) => {
    const layout = getMultiLevelColumns([['Country', 'CompanyName'], ['CustomerID', 'OrderDate', 'OrderID']]);
    grid.setColumnLayout(layout);

    fetch('/data/en/en/editor_demoData.json')
      .then(response => response.json())
      .then(data => {
        grid.getDataSource().setRows(data);
      });
  }
}, {
  fields
});

window.gridView = gridView;
window.dataProvider = dataProvider;
```

```js
var layout_Org = [
   {
     name: "companyGroup",
     direction: "vertical",
     items: [
         {
            
             items: [
                 {
                     column: "Country",
                     cellSpan: 3;
                     width: 50
                 },
                 50,50,
                 {
                     column: "CompanyName",
                     cellSpan: 3;
                     width: 50
                 },
                 50,50,
             ],
             header: false
         },
         {
             items: [
                 {
                     column: "CustomerID",
                     cellSpan: 2;
                     width: 50
                 },
                 50,
                 {
                     column: "OrderDate",
                     cellSpan: 2;
                     width: 50
                 },
                 50,
                 {
                     column: "OrderID",
                     cellSpan: 2;
                     width: 50
                 },
                 50,
             ],
             header: false
         }
     ]
}
];

gridView.setColumnLayout(layout_Org);
```

The function() that can make this layout a little more convenient is as follows.

```js
function getMultiLevelColumns (colNames) {
"use strict";

function leastCommonMultiple(arr) {
var result = lcm(arr[0], arr[1]);

for (var i = 2; i < arr.length; i++) {
result = lcm(result, arr[i]);
}

return result;
};

function gcd(a, b) {
while (b != 0) {
var temp = a % b;
a = b;
b = temp;
}

return a;
};

function lcm(a, b) {
return a * b / gcd(a, b);
};

//var arrLen = colNames.map(x => x.length); //Length of each array
var arrLen = colNames.map(function (x) { return x.length }); //Length of each array
var lcmValue = leastCommonMultiple(arrLen); //least common multiple

var parentGrpColObj = {}
parentGrpColObj.direction = "vertical";
parentGrpColObj.items = [];
parentGrpColObj.header = { visible: false };

for (var i = 0; i < colNames.length; i++) {
var grpColObj = {};
grpColObj.direction = "horizontal";
grpColObj.items = [];
grpColObj.header = {visible: false};

for (var j = 0; j < colNames[i].length ; j++) {
var cellSpan = lcmValue / colNames[i].length;;
var column = {};

column.column = colNames[i][j];
column.cellSpan = cellSpan;
column. width = 50;

grpColObj.items.push(column);
for (var k = 0; k < cellSpan - 1; k++) {
grpColObj.items.push(50);
}
}
parentGrpColObj.items.push(grpColObj);
}

return [parentGrpColObj];
}

//Pass column names by group to array
var layout = getMultiLevelColumns([["Country", "CompanyName"], ["CustomerID", "OrderDate", "OrderID"]]);
gridView.setColumnLayout(layout);
```

### Automatic adjustment of column width and one-line view

* If there is space left after the column is filled in the grid, you can fill the empty area by increasing the size of the column.
For more information, check out the [Automatically Adjust Column Width](/guides/column/grid-fitting) demo.

* If you want to display a grid consisting of a column layout in one line, you can simply implement it with [gridView.linearizeColumns()](/refs/Class/GridBase#linearizecolumns).

```js
const fields = [
  { fieldName: 'OrderID', dataType: 'text' },
  { fieldName: 'CustomerID', dataType: 'text' },
  { fieldName: 'EmployeeID', dataType: 'text' },
  { fieldName: 'OrderDate', dataType: 'datetime', datetimeFormat: 'yyyy-MM-dd', amText: 'AM', pmText: 'PM' },
  { fieldName: 'CompanyName', dataType: 'text' },
  { fieldName: 'Country', dataType: 'text' },
  { fieldName: 'Phone', dataType: 'text' },
  { fieldName: 'ProductName', dataType: 'text' },
  { fieldName: 'QuantityPerUnit', dataType: 'text' },
  { fieldName: 'Quantity', dataType: 'number' },
  { fieldName: 'UnitPrice', dataType: 'number' }
];

const columns = [
  { name: 'Country', fieldName: 'Country', type: 'data', width: '100', header: { text: 'Country' } },
  { name: 'CompanyName', fieldName: 'CompanyName', type: 'data', width: '120', header: { text: 'CompanyName' } },
  { name: 'OrderID', fieldName: 'OrderID', type: 'data', width: '80', header: { text: 'Order ID' } },
  { name: 'CustomerID', fieldName: 'CustomerID', type: 'data', width: '90', header: { text: 'Customer ID' } },
  { name: 'EmployeeID', fieldName: 'EmployeeID', type: 'data', width: '90', header: { text: 'Employee ID' } },
  { name: 'OrderDate', fieldName: 'OrderDate', type: 'data', width: '130', header: { text: 'Order Date' } },
  { name: 'Phone', fieldName: 'Phone', type: 'data', width: '100', header: { text: 'Phone' } },
  { name: 'ProductName', fieldName: 'ProductName', type: 'data', width: '200', header: { text: 'Product Name' } },
  { name: 'QuantityPerUnit', fieldName: 'QuantityPerUnit', type: 'data', width: '100', header: { text: 'Quantity / Unit' }, numberFormat: '#,##0' },
  { name: 'Quantity', fieldName: 'Quantity', type: 'data', width: '100', header: { text: 'Quantity' }, numberFormat: '#,##0' },
  { name: 'UnitPrice', fieldName: 'UnitPrice', type: 'data', width: '100', header: { text: 'Unit Price' }, numberFormat: '#,##0' }
];

var layout;

function getMultiLevelColumns(colNames) {
  function leastCommonMultiple(arr) {
    function gcd(a, b) {
      while (b != 0) {
        var temp = a % b;
        a = b;
        b = temp;
      }
      return a;
    }

    function lcm(a, b) {
      return a * b / gcd(a, b);
    }

    var result = lcm(arr[0], arr[1]);
    for (var i = 2; i < arr.length; i++) {
      result = lcm(result, arr[i]);
    }
    return result;
  }

  var arrLen = colNames.map(function (x) { return x.length });
  var lcmValue = leastCommonMultiple(arrLen);

  var parentGrpColObj = {};
  parentGrpColObj.direction = 'vertical';
  parentGrpColObj.items = [];
  parentGrpColObj.header = { visible: false };

  for (var i = 0; i < colNames.length; i++) {
    var grpColObj = {};
    grpColObj.direction = 'horizontal';
    grpColObj.items = [];
    grpColObj.header = { visible: false };

    for (var j = 0; j < colNames[i].length; j++) {
      var cellSpan = lcmValue / colNames[i].length;
      var column = {};

      column.column = colNames[i][j];
      column.cellSpan = cellSpan;
      column.width = 50;

      grpColObj.items.push(column);
      for (var k = 0; k < cellSpan - 1; k++) {
        grpColObj.items.push(50);
      }
    }
    parentGrpColObj.items.push(grpColObj);
  }

  return [parentGrpColObj];
}

const { gridView, dataProvider } = RealGrid.createGrid('realgrid', {
  columns,
  header: {
    height: -1
  },
  display: {
    rowHeight: 36,
    emptyMessage: 'No data to display.'
  },
  footer: {
    height: 40
  },
  stateBar: {
    width: 16
  },
  onInitialized: (grid) => {
    layout = getMultiLevelColumns([['Country', 'CompanyName'], ['CustomerID', 'OrderDate', 'OrderID']]);
    grid.setColumnLayout(layout);

    fetch('/data/en/editor_demoData.json')
      .then(response => response.json())
      .then(data => {
        grid.getDataSource().setRows(data);
      });
  }
}, {
  fields
});

window.gridView = gridView;
window.dataProvider = dataProvider;

window.fitStyle = function() {
  gridView.displayOptions.fitStyle = 'even';
};

window.linearizeColumns = function() {
  gridView.linearizeColumns();
};

window.setColumnlayout = function() {
  gridView.setColumnLayout(layout);
};
```

```js
function fitStyle() {
   gridView.displayOptions.fitStyle = "even";
}

function linearizeColumns() {
   gridView.linearizeColumns();
}

function setColumnlayout() {
   gridView.setColumnLayout(layout);
}
```