tablib_dataset.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745
  1. // Package tablib is a format-agnostic tabular Dataset library, written in Go.
  2. // It allows you to import, export, and manipulate tabular data sets.
  3. // Advanced features include, dynamic columns, tags & filtering, and seamless format import & export.
  4. package tablib
  5. import (
  6. "fmt"
  7. "sort"
  8. "time"
  9. )
  10. // Dataset represents a set of data, which is a list of data and header for each column.
  11. type Dataset struct {
  12. // EmptyValue represents the string value to b output if a field cannot be
  13. // formatted as a string during output of certain formats.
  14. EmptyValue string
  15. headers []string
  16. data [][]interface{}
  17. tags [][]string
  18. constraints []ColumnConstraint
  19. rows int
  20. cols int
  21. ValidationErrors []ValidationError
  22. }
  23. // DynamicColumn represents a function that can be evaluated dynamically
  24. // when exporting to a predefined format.
  25. type DynamicColumn func([]interface{}) interface{}
  26. // ColumnConstraint represents a function that is bound as a constraint to
  27. // the column so that it can validate its value
  28. type ColumnConstraint func(interface{}) bool
  29. // ValidationError holds the position of a value in the Dataset that have failed
  30. // to validate a constraint.
  31. type ValidationError struct {
  32. Row int
  33. Column int
  34. }
  35. // NewDataset creates a new Dataset.
  36. func NewDataset(headers []string) *Dataset {
  37. return NewDatasetWithData(headers, nil)
  38. }
  39. // NewDatasetWithData creates a new Dataset.
  40. func NewDatasetWithData(headers []string, data [][]interface{}) *Dataset {
  41. d := &Dataset{"", headers, data, make([][]string, 0), make([]ColumnConstraint,
  42. len(headers)), len(data), len(headers), nil}
  43. return d
  44. }
  45. // Headers return the headers of the Dataset.
  46. func (d *Dataset) Headers() []string {
  47. return d.headers
  48. }
  49. // Width returns the number of columns in the Dataset.
  50. func (d *Dataset) Width() int {
  51. return d.cols
  52. }
  53. // Height returns the number of rows in the Dataset.
  54. func (d *Dataset) Height() int {
  55. return d.rows
  56. }
  57. // Append appends a row of values to the Dataset.
  58. func (d *Dataset) Append(row []interface{}) error {
  59. if len(row) != d.cols {
  60. return ErrInvalidDimensions
  61. }
  62. d.data = append(d.data, row)
  63. d.tags = append(d.tags, make([]string, 0))
  64. d.rows++
  65. return nil
  66. }
  67. // AppendTagged appends a row of values to the Dataset with one or multiple tags
  68. // for filtering purposes.
  69. func (d *Dataset) AppendTagged(row []interface{}, tags ...string) error {
  70. if err := d.Append(row); err != nil {
  71. return err
  72. }
  73. d.tags[d.rows-1] = tags[:]
  74. return nil
  75. }
  76. // AppendValues appends a row of values to the Dataset.
  77. func (d *Dataset) AppendValues(row ...interface{}) error {
  78. return d.Append(row[:])
  79. }
  80. // AppendValuesTagged appends a row of values to the Dataset with one or multiple tags
  81. // for filtering purposes.
  82. func (d *Dataset) AppendValuesTagged(row ...interface{}) error {
  83. if len(row) < d.cols {
  84. return ErrInvalidDimensions
  85. }
  86. var tags []string
  87. for _, tag := range row[d.cols:] {
  88. if tagStr, ok := tag.(string); ok {
  89. tags = append(tags, tagStr)
  90. } else {
  91. return ErrInvalidTag
  92. }
  93. }
  94. return d.AppendTagged(row[:d.cols], tags...)
  95. }
  96. // Insert inserts a row at a given index.
  97. func (d *Dataset) Insert(index int, row []interface{}) error {
  98. if index < 0 || index >= d.rows {
  99. return ErrInvalidRowIndex
  100. }
  101. if len(row) != d.cols {
  102. return ErrInvalidDimensions
  103. }
  104. ndata := make([][]interface{}, 0, d.rows+1)
  105. ndata = append(ndata, d.data[:index]...)
  106. ndata = append(ndata, row)
  107. ndata = append(ndata, d.data[index:]...)
  108. d.data = ndata
  109. d.rows++
  110. ntags := make([][]string, 0, d.rows+1)
  111. ntags = append(ntags, d.tags[:index]...)
  112. ntags = append(ntags, make([]string, 0))
  113. ntags = append(ntags, d.tags[index:]...)
  114. d.tags = ntags
  115. return nil
  116. }
  117. // InsertValues inserts a row of values at a given index.
  118. func (d *Dataset) InsertValues(index int, values ...interface{}) error {
  119. return d.Insert(index, values[:])
  120. }
  121. // InsertTagged inserts a row at a given index with specific tags.
  122. func (d *Dataset) InsertTagged(index int, row []interface{}, tags ...string) error {
  123. if err := d.Insert(index, row); err != nil {
  124. return err
  125. }
  126. d.Insert(index, row)
  127. d.tags[index] = tags[:]
  128. return nil
  129. }
  130. // Tag tags a row at a given index with specific tags.
  131. // Returns ErrInvalidRowIndex if the row does not exist.
  132. func (d *Dataset) Tag(index int, tags ...string) error {
  133. if index < 0 || index >= d.rows {
  134. return ErrInvalidRowIndex
  135. }
  136. for _, tag := range tags {
  137. if !isTagged(tag, d.tags[index]) {
  138. d.tags[index] = append(d.tags[index], tag)
  139. }
  140. }
  141. return nil
  142. }
  143. // Tags returns the tags of a row at a given index.
  144. // Returns ErrInvalidRowIndex if the row does not exist.
  145. func (d *Dataset) Tags(index int) ([]string, error) {
  146. if index < 0 || index >= d.rows {
  147. return nil, ErrInvalidRowIndex
  148. }
  149. return d.tags[index], nil
  150. }
  151. // AppendColumn appends a new column with values to the Dataset.
  152. func (d *Dataset) AppendColumn(header string, cols []interface{}) error {
  153. if len(cols) != d.rows {
  154. return ErrInvalidDimensions
  155. }
  156. d.headers = append(d.headers, header)
  157. d.constraints = append(d.constraints, nil) // no constraint by default
  158. d.cols++
  159. for i, e := range d.data {
  160. d.data[i] = append(e, cols[i])
  161. }
  162. return nil
  163. }
  164. // AppendConstrainedColumn appends a constrained column to the Dataset.
  165. func (d *Dataset) AppendConstrainedColumn(header string, constraint ColumnConstraint, cols []interface{}) error {
  166. err := d.AppendColumn(header, cols)
  167. if err != nil {
  168. return err
  169. }
  170. d.constraints[d.cols-1] = constraint
  171. return nil
  172. }
  173. // AppendColumnValues appends a new column with values to the Dataset.
  174. func (d *Dataset) AppendColumnValues(header string, cols ...interface{}) error {
  175. return d.AppendColumn(header, cols[:])
  176. }
  177. // AppendDynamicColumn appends a dynamic column to the Dataset.
  178. func (d *Dataset) AppendDynamicColumn(header string, fn DynamicColumn) {
  179. d.headers = append(d.headers, header)
  180. d.constraints = append(d.constraints, nil)
  181. d.cols++
  182. for i, e := range d.data {
  183. d.data[i] = append(e, fn)
  184. }
  185. }
  186. // ConstrainColumn adds a constraint to a column in the Dataset.
  187. func (d *Dataset) ConstrainColumn(header string, constraint ColumnConstraint) {
  188. i := indexOfColumn(header, d)
  189. if i != -1 {
  190. d.constraints[i] = constraint
  191. }
  192. }
  193. // InsertColumn insert a new column at a given index.
  194. func (d *Dataset) InsertColumn(index int, header string, cols []interface{}) error {
  195. if index < 0 || index >= d.cols {
  196. return ErrInvalidColumnIndex
  197. }
  198. if len(cols) != d.rows {
  199. return ErrInvalidDimensions
  200. }
  201. d.insertHeader(index, header)
  202. // for each row, insert the column
  203. for i, r := range d.data {
  204. row := make([]interface{}, 0, d.cols)
  205. row = append(row, r[:index]...)
  206. row = append(row, cols[i])
  207. row = append(row, r[index:]...)
  208. d.data[i] = row
  209. }
  210. return nil
  211. }
  212. // InsertDynamicColumn insert a new dynamic column at a given index.
  213. func (d *Dataset) InsertDynamicColumn(index int, header string, fn DynamicColumn) error {
  214. if index < 0 || index >= d.cols {
  215. return ErrInvalidColumnIndex
  216. }
  217. d.insertHeader(index, header)
  218. // for each row, insert the column
  219. for i, r := range d.data {
  220. row := make([]interface{}, 0, d.cols)
  221. row = append(row, r[:index]...)
  222. row = append(row, fn)
  223. row = append(row, r[index:]...)
  224. d.data[i] = row
  225. }
  226. return nil
  227. }
  228. // InsertConstrainedColumn insert a new constrained column at a given index.
  229. func (d *Dataset) InsertConstrainedColumn(index int, header string,
  230. constraint ColumnConstraint, cols []interface{}) error {
  231. err := d.InsertColumn(index, header, cols)
  232. if err != nil {
  233. return err
  234. }
  235. d.constraints[index] = constraint
  236. return nil
  237. }
  238. // insertHeader inserts a header at a specific index.
  239. func (d *Dataset) insertHeader(index int, header string) {
  240. headers := make([]string, 0, d.cols+1)
  241. headers = append(headers, d.headers[:index]...)
  242. headers = append(headers, header)
  243. headers = append(headers, d.headers[index:]...)
  244. d.headers = headers
  245. constraints := make([]ColumnConstraint, 0, d.cols+1)
  246. constraints = append(constraints, d.constraints[:index]...)
  247. constraints = append(constraints, nil)
  248. constraints = append(constraints, d.constraints[index:]...)
  249. d.constraints = constraints
  250. d.cols++
  251. }
  252. // ValidFailFast returns whether the Dataset is valid regarding constraints that have
  253. // been previously set on columns.
  254. func (d *Dataset) ValidFailFast() bool {
  255. valid := true
  256. for column, constraint := range d.constraints {
  257. if constraint != nil {
  258. for row, val := range d.Column(d.headers[column]) {
  259. cellIsValid := true
  260. switch val.(type) {
  261. case DynamicColumn:
  262. cellIsValid = constraint((val.(DynamicColumn))(d.data[row]))
  263. default:
  264. cellIsValid = constraint(val)
  265. }
  266. if !cellIsValid {
  267. valid = false
  268. break
  269. }
  270. }
  271. }
  272. }
  273. if valid {
  274. d.ValidationErrors = make([]ValidationError, 0)
  275. }
  276. return valid
  277. }
  278. // Valid returns whether the Dataset is valid regarding constraints that have
  279. // been previously set on columns.
  280. // Its behaviour is different of ValidFailFast in a sense that it will validate the whole
  281. // Dataset and all the validation errors will be available by using Dataset.ValidationErrors
  282. func (d *Dataset) Valid() bool {
  283. d.ValidationErrors = make([]ValidationError, 0)
  284. valid := true
  285. for column, constraint := range d.constraints {
  286. if constraint != nil {
  287. for row, val := range d.Column(d.headers[column]) {
  288. cellIsValid := true
  289. switch val.(type) {
  290. case DynamicColumn:
  291. cellIsValid = constraint((val.(DynamicColumn))(d.data[row]))
  292. default:
  293. cellIsValid = constraint(val)
  294. }
  295. if !cellIsValid {
  296. d.ValidationErrors = append(d.ValidationErrors,
  297. ValidationError{Row: row, Column: column})
  298. valid = false
  299. }
  300. }
  301. }
  302. }
  303. return valid
  304. }
  305. // HasAnyConstraint returns whether the Dataset has any constraint set.
  306. func (d *Dataset) HasAnyConstraint() bool {
  307. hasConstraint := false
  308. for _, constraint := range d.constraints {
  309. if constraint != nil {
  310. hasConstraint = true
  311. break
  312. }
  313. }
  314. return hasConstraint
  315. }
  316. // ValidSubset return a new Dataset containing only the rows validating their
  317. // constraints. This is similar to what Filter() does with tags, but with constraints.
  318. // If no constraints are set, it returns the same instance.
  319. // Note: The returned Dataset is free of any constraints, tags are conserved.
  320. func (d *Dataset) ValidSubset() *Dataset {
  321. return d.internalValidSubset(true)
  322. }
  323. // InvalidSubset return a new Dataset containing only the rows failing to validate their
  324. // constraints.
  325. // If no constraints are set, it returns the same instance.
  326. // Note: The returned Dataset is free of any constraints, tags are conserved.
  327. func (d *Dataset) InvalidSubset() *Dataset {
  328. return d.internalValidSubset(false)
  329. }
  330. // internalValidSubset return a new Dataset containing only the rows validating their
  331. // constraints or not depending on its parameter `valid`.
  332. func (d *Dataset) internalValidSubset(valid bool) *Dataset {
  333. if !d.HasAnyConstraint() {
  334. return d
  335. }
  336. nd := NewDataset(d.headers)
  337. nd.data = make([][]interface{}, 0)
  338. ndRowIndex := 0
  339. nd.tags = make([][]string, 0)
  340. for i, row := range d.data {
  341. keep := true
  342. for j, val := range d.data[i] {
  343. if d.constraints[j] != nil {
  344. switch val.(type) {
  345. case DynamicColumn:
  346. if valid {
  347. keep = d.constraints[j]((val.(DynamicColumn))(row))
  348. } else {
  349. keep = !d.constraints[j]((val.(DynamicColumn))(row))
  350. }
  351. default:
  352. if valid {
  353. keep = d.constraints[j](val)
  354. } else {
  355. keep = !d.constraints[j](val)
  356. }
  357. }
  358. }
  359. if valid && !keep {
  360. break
  361. }
  362. }
  363. if keep {
  364. nd.data = append(nd.data, make([]interface{}, 0, nd.cols))
  365. nd.data[ndRowIndex] = append(nd.data[ndRowIndex], row...)
  366. nd.tags = append(nd.tags, make([]string, 0, nd.cols))
  367. nd.tags[ndRowIndex] = append(nd.tags[ndRowIndex], d.tags[i]...)
  368. ndRowIndex++
  369. }
  370. }
  371. nd.cols = d.cols
  372. nd.rows = ndRowIndex
  373. return nd
  374. }
  375. // Stack stacks two Dataset by joining at the row level, and return new combined Dataset.
  376. func (d *Dataset) Stack(other *Dataset) (*Dataset, error) {
  377. if d.Width() != other.Width() {
  378. return nil, ErrInvalidDimensions
  379. }
  380. nd := NewDataset(d.headers)
  381. nd.cols = d.cols
  382. nd.rows = d.rows + other.rows
  383. nd.tags = make([][]string, 0, nd.rows)
  384. nd.tags = append(nd.tags, d.tags...)
  385. nd.tags = append(nd.tags, other.tags...)
  386. nd.data = make([][]interface{}, 0, nd.rows)
  387. nd.data = append(nd.data, d.data...)
  388. nd.data = append(nd.data, other.data...)
  389. return nd, nil
  390. }
  391. // StackColumn stacks two Dataset by joining them at the column level, and return new combined Dataset.
  392. func (d *Dataset) StackColumn(other *Dataset) (*Dataset, error) {
  393. if d.Height() != other.Height() {
  394. return nil, ErrInvalidDimensions
  395. }
  396. nheaders := d.headers
  397. nheaders = append(nheaders, other.headers...)
  398. nd := NewDataset(nheaders)
  399. nd.cols = d.cols + nd.cols
  400. nd.rows = d.rows
  401. nd.data = make([][]interface{}, nd.rows, nd.rows)
  402. nd.tags = make([][]string, nd.rows, nd.rows)
  403. for i := range d.data {
  404. nd.data[i] = make([]interface{}, 0, nd.cols)
  405. nd.data[i] = append(nd.data[i], d.data[i]...)
  406. nd.data[i] = append(nd.data[i], other.data[i]...)
  407. nd.tags[i] = make([]string, 0, nd.cols)
  408. nd.tags[i] = append(nd.tags[i], d.tags[i]...)
  409. nd.tags[i] = append(nd.tags[i], other.tags[i]...)
  410. }
  411. return nd, nil
  412. }
  413. // Column returns all the values for a specific column
  414. // returns nil if column is not found.
  415. func (d *Dataset) Column(header string) []interface{} {
  416. colIndex := indexOfColumn(header, d)
  417. if colIndex == -1 {
  418. return nil
  419. }
  420. values := make([]interface{}, d.rows)
  421. for i, e := range d.data {
  422. switch e[colIndex].(type) {
  423. case DynamicColumn:
  424. values[i] = e[colIndex].(DynamicColumn)(e)
  425. default:
  426. values[i] = e[colIndex]
  427. }
  428. }
  429. return values
  430. }
  431. // Row returns a map representing a specific row of the Dataset.
  432. // returns tablib.ErrInvalidRowIndex if the row cannot be found
  433. func (d *Dataset) Row(index int) (map[string]interface{}, error) {
  434. if index < 0 || index >= d.rows {
  435. return nil, ErrInvalidRowIndex
  436. }
  437. row := make(map[string]interface{})
  438. for i, e := range d.data[index] {
  439. switch e.(type) {
  440. case DynamicColumn:
  441. row[d.headers[i]] = e.(DynamicColumn)(d.data[index])
  442. default:
  443. row[d.headers[i]] = e
  444. }
  445. }
  446. return row, nil
  447. }
  448. // Rows returns an array of map representing a set of specific rows of the Dataset.
  449. // returns tablib.ErrInvalidRowIndex if the row cannot be found.
  450. func (d *Dataset) Rows(index ...int) ([]map[string]interface{}, error) {
  451. for _, i := range index {
  452. if i < 0 || i >= d.rows {
  453. return nil, ErrInvalidRowIndex
  454. }
  455. }
  456. rows := make([]map[string]interface{}, 0, len(index))
  457. for _, i := range index {
  458. row, _ := d.Row(i)
  459. rows = append(rows, row)
  460. }
  461. return rows, nil
  462. }
  463. // Slice returns a new Dataset representing a slice of the orignal Dataset like a slice of an array.
  464. // returns tablib.ErrInvalidRowIndex if the lower or upper bound is out of range.
  465. func (d *Dataset) Slice(lower, upperNonInclusive int) (*Dataset, error) {
  466. if lower > upperNonInclusive || lower < 0 || upperNonInclusive > d.rows {
  467. return nil, ErrInvalidRowIndex
  468. }
  469. rowCount := upperNonInclusive - lower
  470. cols := d.cols
  471. nd := NewDataset(d.headers)
  472. nd.data = make([][]interface{}, 0, rowCount)
  473. nd.tags = make([][]string, 0, rowCount)
  474. nd.rows = upperNonInclusive - lower
  475. j := 0
  476. for i := lower; i < upperNonInclusive; i++ {
  477. nd.data = append(nd.data, make([]interface{}, 0, cols))
  478. nd.data[j] = make([]interface{}, 0, cols)
  479. nd.data[j] = append(nd.data[j], d.data[i]...)
  480. nd.tags = append(nd.tags, make([]string, 0, cols))
  481. nd.tags[j] = make([]string, 0, cols)
  482. nd.tags[j] = append(nd.tags[j], d.tags[i]...)
  483. j++
  484. }
  485. return nd, nil
  486. }
  487. // Filter filters a Dataset, returning a fresh Dataset including only the rows
  488. // previously tagged with one of the given tags. Returns a new Dataset.
  489. func (d *Dataset) Filter(tags ...string) *Dataset {
  490. nd := NewDataset(d.headers)
  491. for rowIndex, rowValue := range d.data {
  492. for _, filterTag := range tags {
  493. if isTagged(filterTag, d.tags[rowIndex]) {
  494. nd.AppendTagged(rowValue, d.tags[rowIndex]...) // copy tags
  495. }
  496. }
  497. }
  498. return nd
  499. }
  500. // Sort sorts the Dataset by a specific column. Returns a new Dataset.
  501. func (d *Dataset) Sort(column string) *Dataset {
  502. return d.internalSort(column, false)
  503. }
  504. // SortReverse sorts the Dataset by a specific column in reverse order. Returns a new Dataset.
  505. func (d *Dataset) SortReverse(column string) *Dataset {
  506. return d.internalSort(column, true)
  507. }
  508. func (d *Dataset) internalSort(column string, reverse bool) *Dataset {
  509. nd := NewDataset(d.headers)
  510. pairs := make([]entryPair, 0, nd.rows)
  511. for i, v := range d.Column(column) {
  512. pairs = append(pairs, entryPair{i, v})
  513. }
  514. var how sort.Interface
  515. // sort by column
  516. switch pairs[0].value.(type) {
  517. case string:
  518. how = byStringValue(pairs)
  519. case int:
  520. how = byIntValue(pairs)
  521. case int64:
  522. how = byInt64Value(pairs)
  523. case uint64:
  524. how = byUint64Value(pairs)
  525. case float64:
  526. how = byFloatValue(pairs)
  527. case time.Time:
  528. how = byTimeValue(pairs)
  529. default:
  530. // nothing
  531. }
  532. if !reverse {
  533. sort.Sort(how)
  534. } else {
  535. sort.Sort(sort.Reverse(how))
  536. }
  537. // now iterate on the pairs and add the data sorted to the new Dataset
  538. for _, p := range pairs {
  539. nd.AppendTagged(d.data[p.index], d.tags[p.index]...)
  540. }
  541. return nd
  542. }
  543. // Transpose transposes a Dataset, turning rows into columns and vice versa,
  544. // returning a new Dataset instance. The first row of the original instance
  545. // becomes the new header row. Tags, constraints and dynamic columns are lost
  546. // in the returned Dataset.
  547. // TODO
  548. func (d *Dataset) Transpose() *Dataset {
  549. newHeaders := make([]string, 0, d.cols+1)
  550. newHeaders = append(newHeaders, d.headers[0])
  551. for _, c := range d.Column(d.headers[0]) {
  552. newHeaders = append(newHeaders, d.asString(c))
  553. }
  554. nd := NewDataset(newHeaders)
  555. nd.data = make([][]interface{}, 0, d.cols)
  556. for i := 1; i < d.cols; i++ {
  557. nd.data = append(nd.data, make([]interface{}, 0, d.rows))
  558. nd.data[i-1] = make([]interface{}, 0, d.rows)
  559. nd.data[i-1] = append(nd.data[i-1], d.headers[i])
  560. nd.data[i-1] = append(nd.data[i-1], d.Column(d.headers[i])...)
  561. }
  562. nd.rows = d.cols - 1
  563. return nd
  564. }
  565. // DeleteRow deletes a row at a specific index
  566. func (d *Dataset) DeleteRow(row int) error {
  567. if row < 0 || row >= d.rows {
  568. return ErrInvalidRowIndex
  569. }
  570. d.data = append(d.data[:row], d.data[row+1:]...)
  571. d.rows--
  572. return nil
  573. }
  574. // DeleteColumn deletes a column from the Dataset.
  575. func (d *Dataset) DeleteColumn(header string) error {
  576. colIndex := indexOfColumn(header, d)
  577. if colIndex == -1 {
  578. return ErrInvalidColumnIndex
  579. }
  580. d.cols--
  581. d.headers = append(d.headers[:colIndex], d.headers[colIndex+1:]...)
  582. // remove the column
  583. for i := range d.data {
  584. d.data[i] = append(d.data[i][:colIndex], d.data[i][colIndex+1:]...)
  585. }
  586. return nil
  587. }
  588. func indexOfColumn(header string, d *Dataset) int {
  589. for i, e := range d.headers {
  590. if e == header {
  591. return i
  592. }
  593. }
  594. return -1
  595. }
  596. // Dict returns the Dataset as an array of map where each key is a column.
  597. func (d *Dataset) Dict() []interface{} {
  598. back := make([]interface{}, d.rows)
  599. for i, e := range d.data {
  600. m := make(map[string]interface{}, d.cols-1)
  601. for j, c := range d.headers {
  602. switch e[j].(type) {
  603. case DynamicColumn:
  604. m[c] = e[j].(DynamicColumn)(e)
  605. default:
  606. m[c] = e[j]
  607. }
  608. }
  609. back[i] = m
  610. }
  611. return back
  612. }
  613. // Records returns the Dataset as an array of array where each entry is a string.
  614. // The first row of the returned 2d array represents the columns of the Dataset.
  615. func (d *Dataset) Records() [][]string {
  616. records := make([][]string, d.rows+1 /* +1 for header */)
  617. records[0] = make([]string, d.cols)
  618. for j, e := range d.headers {
  619. records[0][j] = e
  620. }
  621. for i, e := range d.data {
  622. rowIndex := i + 1
  623. j := 0
  624. records[rowIndex] = make([]string, d.cols)
  625. for _, v := range e {
  626. vv := v
  627. switch v.(type) {
  628. case DynamicColumn:
  629. vv = v.(DynamicColumn)(e)
  630. default:
  631. // nothing
  632. }
  633. records[rowIndex][j] = d.asString(vv)
  634. j++
  635. }
  636. }
  637. return records
  638. }
  639. // ffs
  640. func justLetMeKeepFmt() {
  641. fmt.Printf("")
  642. }