@@ -56,6 +56,12 @@ const npm = {
5656 *
5757 * By default, only the object's own properties are enumerated for column names.
5858 *
59+ * @param {'error' | 'skip' | 'replace' } [options.duplicate = 'error']
60+ * Strategy for processing duplicate columns:
61+ * - `'error'` - throws {@link external:Error Error} = `Duplicate column name "name".` (Default)
62+ * - `'skip'` - skips repeated columns (only the first will be used)
63+ * - `'replace'` - replaces repeated columns (the last one will be used)
64+ *
5965 * @returns {helpers.ColumnSet }
6066 *
6167 * @see
@@ -166,17 +172,17 @@ const npm = {
166172 */
167173class ColumnSet extends InnerState {
168174
169- constructor ( columns , opt ) {
175+ constructor ( columns , options ) {
170176 super ( ) ;
171177
172178 if ( ! columns || typeof columns !== 'object' ) {
173179 throw new TypeError ( 'Invalid parameter \'columns\' specified.' ) ;
174180 }
175181
176- opt = assert ( opt , [ 'table' , 'inherit' ] ) ;
182+ options = assert ( options , [ 'table' , 'inherit' , 'duplicate '] ) ;
177183
178- if ( ! npm . utils . isNull ( opt . table ) ) {
179- this . table = ( opt . table instanceof TableName ) ? opt . table : new TableName ( opt . table ) ;
184+ if ( ! npm . utils . isNull ( options . table ) ) {
185+ this . table = ( options . table instanceof TableName ) ? options . table : new TableName ( options . table ) ;
180186 }
181187
182188 /**
@@ -190,6 +196,7 @@ class ColumnSet extends InnerState {
190196 * - **secondary:** to be automatically written into the console (for logging purposes).
191197 */
192198
199+ this . columns = [ ] ;
193200
194201 /**
195202 * @name helpers.ColumnSet#columns
@@ -199,22 +206,28 @@ class ColumnSet extends InnerState {
199206 * Array of {@link helpers.Column Column} objects.
200207 */
201208 if ( Array . isArray ( columns ) ) {
202- const colNames = { } ;
203- this . columns = columns . map ( c => {
209+ const duplicate = options . duplicate || 'error' ;
210+ columns . forEach ( c => {
204211 const col = ( c instanceof Column ) ? c : new Column ( c ) ;
205- if ( col . name in colNames ) {
206- throw new Error ( `Duplicate column name "${ col . name } ".` ) ;
212+ const idx = this . columns . findIndex ( a => a . name === col . name ) ;
213+ if ( idx === - 1 ) {
214+ this . columns . push ( col ) ;
215+ } else {
216+ if ( duplicate === 'replace' ) {
217+ this . columns [ idx ] = col ;
218+ return ;
219+ }
220+ if ( duplicate !== 'skip' ) {
221+ throw new Error ( `Duplicate column name "${ col . name } ".` ) ;
222+ }
207223 }
208- colNames [ col . name ] = true ;
209- return col ;
210224 } ) ;
211225 } else {
212226 if ( columns instanceof Column ) {
213227 this . columns = [ columns ] ;
214228 } else {
215- this . columns = [ ] ;
216229 for ( const name in columns ) {
217- if ( opt . inherit || Object . prototype . hasOwnProperty . call ( columns , name ) ) {
230+ if ( options . inherit || Object . prototype . hasOwnProperty . call ( columns , name ) ) {
218231 this . columns . push ( new Column ( name ) ) ;
219232 }
220233 }
@@ -415,13 +428,18 @@ ColumnSet.prototype.assignColumns = function (options) {
415428 * @description
416429 * Creates a new {@link helpers.ColumnSet ColumnSet}, by joining the two sets of columns.
417430 *
418- * If the two sets contain a column with the same `name` (case-sensitive), an error is thrown.
431+ * If the two sets contain a column with the same `name` (case-sensitive), an error is thrown (unless `opts.skip` is set to `true`) .
419432 *
420433 * @param {helpers.Column|helpers.ColumnSet|array } columns
421434 * Columns to be appended, of the same type as parameter `columns` during {@link helpers.ColumnSet ColumnSet} construction, except:
422435 * - it can also be of type {@link helpers.ColumnSet ColumnSet}
423436 * - it cannot be a simple object (properties enumeration is not supported here)
424437 *
438+ * @param {object } [options]
439+ *
440+ * @param {boolean } [options.skip = false]
441+ * When set to `true`, it skips duplicate columns (by default, it throws an error).
442+ *
425443 * @returns {helpers.ColumnSet }
426444 * New {@link helpers.ColumnSet ColumnSet} object with the extended/concatenated list of columns.
427445 *
@@ -465,13 +483,12 @@ ColumnSet.prototype.assignColumns = function (options) {
465483 * // ]
466484 * // }
467485 */
468- ColumnSet . prototype . extend = function ( columns ) {
469- let cs = columns ;
470- if ( ! ( cs instanceof ColumnSet ) ) {
471- cs = new ColumnSet ( columns ) ;
472- }
473- // Any duplicate column will throw Error = 'Duplicate column name "name".',
474- return new ColumnSet ( this . columns . concat ( cs . columns ) , { table : this . table } ) ;
486+ ColumnSet . prototype . extend = function ( columns , options ) {
487+ options = assert ( options , [ 'skip' ] ) ;
488+ const duplicate = options . skip ? 'skip' : 'error' ;
489+ const cs = columns instanceof ColumnSet ? columns : new ColumnSet ( columns , { duplicate} ) ;
490+ const joinedCols = this . columns . concat ( ...cs . columns ) ;
491+ return new ColumnSet ( joinedCols , { table : this . table , duplicate} ) ;
475492} ;
476493
477494/**
@@ -536,23 +553,10 @@ ColumnSet.prototype.extend = function (columns) {
536553 *
537554 */
538555ColumnSet . prototype . merge = function ( columns ) {
539- let cs = columns ;
540- if ( ! ( cs instanceof ColumnSet ) ) {
541- cs = new ColumnSet ( columns ) ;
542- }
543- const colNames = { } , cols = [ ] ;
544- this . columns . forEach ( ( c , idx ) => {
545- cols . push ( c ) ;
546- colNames [ c . name ] = idx ;
547- } ) ;
548- cs . columns . forEach ( c => {
549- if ( c . name in colNames ) {
550- cols [ colNames [ c . name ] ] = c ;
551- } else {
552- cols . push ( c ) ;
553- }
554- } ) ;
555- return new ColumnSet ( cols , { table : this . table } ) ;
556+ const duplicate = 'replace' ;
557+ const cs = columns instanceof ColumnSet ? columns : new ColumnSet ( columns , { duplicate} ) ;
558+ const joinedCols = this . columns . concat ( ...cs . columns ) ;
559+ return new ColumnSet ( joinedCols , { table : this . table , duplicate} ) ;
556560} ;
557561
558562/**
0 commit comments