// Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. import { Vector } from '../vector.js'; import { IntBuilder } from './int.js'; import { Dictionary, DataType } from '../type.js'; import { Builder, BuilderOptions } from '../builder.js'; import { makeBuilder } from '../factories.js'; type DictionaryHashFunction = (x: any) => string | number; export interface DictionaryBuilderOptions extends BuilderOptions { dictionaryHashFunction?: DictionaryHashFunction; } /** @ignore */ export class DictionaryBuilder extends Builder { protected _dictionaryOffset: number; protected _dictionary?: Vector; protected _keysToIndices: { [key: string]: number }; public readonly indices: IntBuilder; public readonly dictionary: Builder; constructor({ 'type': type, 'nullValues': nulls, 'dictionaryHashFunction': hashFn }: DictionaryBuilderOptions) { super({ type: new Dictionary(type.dictionary, type.indices, type.id, type.isOrdered) as T }); this._nulls = null; this._dictionaryOffset = 0; this._keysToIndices = Object.create(null); this.indices = makeBuilder({ 'type': this.type.indices, 'nullValues': nulls }) as IntBuilder; this.dictionary = makeBuilder({ 'type': this.type.dictionary, 'nullValues': null }) as Builder; if (typeof hashFn === 'function') { this.valueToKey = hashFn; } } public get values() { return this.indices.values; } public get nullCount() { return this.indices.nullCount; } public get nullBitmap() { return this.indices.nullBitmap; } public get byteLength() { return this.indices.byteLength + this.dictionary.byteLength; } public get reservedLength() { return this.indices.reservedLength + this.dictionary.reservedLength; } public get reservedByteLength() { return this.indices.reservedByteLength + this.dictionary.reservedByteLength; } public isValid(value: T['TValue'] | TNull) { return this.indices.isValid(value); } public setValid(index: number, valid: boolean) { const indices = this.indices; valid = indices.setValid(index, valid); this.length = indices.length; return valid; } public setValue(index: number, value: T['TValue']) { const keysToIndices = this._keysToIndices; const key = this.valueToKey(value); let idx = keysToIndices[key]; if (idx === undefined) { keysToIndices[key] = idx = this._dictionaryOffset + this.dictionary.append(value).length - 1; } return this.indices.setValue(index, idx); } public flush() { const type = this.type; const prev = this._dictionary; const curr = this.dictionary.toVector(); const data = this.indices.flush().clone(type); data.dictionary = prev ? prev.concat(curr) : curr; this.finished || (this._dictionaryOffset += curr.length); this._dictionary = data.dictionary as Vector; this.clear(); return data; } public finish() { this.indices.finish(); this.dictionary.finish(); this._dictionaryOffset = 0; this._keysToIndices = Object.create(null); return super.finish(); } public clear() { this.indices.clear(); this.dictionary.clear(); return super.clear(); } public valueToKey(val: any): string | number { return typeof val === 'string' ? val : `${val}`; } }