Skip to content

Commit

Permalink
Fix enum meta grid encoding
Browse files Browse the repository at this point in the history
  • Loading branch information
garethj2 committed Mar 15, 2024
1 parent df70a13 commit 1003926
Show file tree
Hide file tree
Showing 2 changed files with 149 additions and 64 deletions.
149 changes: 100 additions & 49 deletions spec/core/EnumTag.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,47 @@ import { EnumTag } from '../../src/core/EnumTag'
import { makeValue } from '../../src/core/util'

describe('EnumTag', () => {
const duplicateNameGrid = HGrid.make([
new HDict({
name: 'a',
code: -1,
}),
new HDict({
name: 'x',
code: -1,
}),
new HDict({
name: 'b',
code: 9,
}),
new HDict({
name: 'b',
code: 10,
}),
])

describe('construction', () => {
it('creates an enum tag from a string', () => {
expect(new EnumTag('a,b,c').encodeToObject()).toEqual({
a: 0,
b: 1,
c: 2,
a: [0],
b: [1],
c: [2],
})
})

it('creates an enum tag from a haystack string', () => {
expect(new EnumTag(HStr.make('a,b,c')).encodeToObject()).toEqual({
a: 0,
b: 1,
c: 2,
a: [0],
b: [1],
c: [2],
})
})

it('creates an enum tag from a string with different indexes', () => {
expect(new EnumTag('a,,,b,,c,,,').encodeToObject()).toEqual({
a: 0,
b: 3,
c: 5,
a: [0],
b: [3],
c: [5],
})
})

Expand All @@ -44,9 +63,23 @@ describe('EnumTag', () => {
}

expect(new EnumTag(obj).encodeToObject()).toEqual({
a: [0],
b: [1],
c: [2],
})
})

it('creates an enum tag from an object with many codes', () => {
const obj = {
a: 0,
b: 1,
c: 2,
c: [2, 3],
}

expect(new EnumTag(obj).encodeToObject()).toEqual({
a: [0],
b: [1],
c: [3, 2],
})
})

Expand All @@ -67,42 +100,12 @@ describe('EnumTag', () => {
])

expect(new EnumTag(grid).encodeToObject()).toEqual({
a: 1,
b: 2,
c: 3,
a: [1],
b: [2],
c: [3],
})
})

it('decode values from a dict with differing codes', () => {
const grid = HGrid.make([
new HDict({
name: 'a',
code: -1,
}),
new HDict({
name: 'x',
code: -1,
}),
new HDict({
name: 'b',
code: 9,
}),
new HDict({
name: 'b',
code: 10,
}),
])

const enumTag = new EnumTag(grid)

expect(enumTag.nameToCode('a')).toBe(-1)
expect(enumTag.nameToCode('x')).toBe(-1)
expect(enumTag.nameToCode('b')).toBe(10)

expect(enumTag.codeToName(9)).toBe('b')
expect(enumTag.codeToName(10)).toBe('b')
})

it('creates an enum tag from a grid with no codes', () => {
const grid = new HGrid([
new HDict({
Expand All @@ -117,9 +120,9 @@ describe('EnumTag', () => {
])

expect(new EnumTag(grid).encodeToObject()).toEqual({
a: 0,
b: 1,
c: 2,
a: [0],
b: [1],
c: [2],
})
})
}) // construction
Expand All @@ -140,6 +143,14 @@ describe('EnumTag', () => {
it('returns undefined when a name cannot be found', () => {
expect(new EnumTag('a,b,c').nameToCode('notFound')).toBe(undefined)
})

it('handles duplicate names', () => {
const enumTag = new EnumTag(duplicateNameGrid)

expect(enumTag.nameToCode('a')).toBe(-1)
expect(enumTag.nameToCode('x')).toBe(-1)
expect(enumTag.nameToCode('b')).toBe(10)
})
}) // #nameToCode()

describe('#codeToName()', () => {
Expand All @@ -158,6 +169,13 @@ describe('EnumTag', () => {
it('returns undefined for a code that cannot be found', () => {
expect(new EnumTag('a,b,c').codeToName(99)).toBe(undefined)
})

it('handles duplicate names', () => {
const enumTag = new EnumTag(duplicateNameGrid)

expect(enumTag.codeToName(9)).toBe('b')
expect(enumTag.codeToName(10)).toBe('b')
})
}) // #codeToName()

describe('#nameToBool()', () => {
Expand Down Expand Up @@ -206,6 +224,14 @@ describe('EnumTag', () => {
it('returns the names', () => {
expect(new EnumTag('a,b,c').names).toEqual(['a', 'b', 'c'])
})

it('returns the names for duplicates', () => {
expect(new EnumTag(duplicateNameGrid).names).toEqual([
'a',
'x',
'b',
])
})
}) // #names

describe('#encodeToString()', () => {
Expand Down Expand Up @@ -239,14 +265,39 @@ describe('EnumTag', () => {
]).toJSON()
)
})

it('encodes enumerations with duplicate names to a grid', () => {
expect(
new EnumTag(duplicateNameGrid).encodeToGrid().toJSON()
).toEqual(
HGrid.make([
new HDict({
name: 'a',
code: -1,
}),
new HDict({
name: 'x',
code: -1,
}),
new HDict({
name: 'b',
code: 10,
}),
new HDict({
name: 'b',
code: 9,
}),
]).toJSON()
)
})
}) // #encodeToGrid()

describe('#encodeToObject()', () => {
it('encode the enumerations to an object', () => {
expect(new EnumTag('a,b,c').encodeToObject()).toEqual({
a: 0,
b: 1,
c: 2,
a: [0],
b: [1],
c: [2],
})
})
}) // #encodeToObject()
Expand Down
64 changes: 49 additions & 15 deletions src/core/EnumTag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,9 @@ export class EnumTag {
*
* @param data A comma separated string of enumerations, enum grid or enum name to code object.
*/
constructor(data: string | HStr | HGrid | Record<string, number>) {
constructor(
data: string | HStr | HGrid | Record<string, number | number[]>
) {
let trueName: string | undefined
let falseName: string | undefined

Expand Down Expand Up @@ -95,8 +97,16 @@ export class EnumTag {
Object.keys(data).forEach((name) => {
const code = data[name]

if (typeof name === 'string' && typeof code === 'number') {
addEnumEntry(name, code)
if (typeof name === 'string') {
if (typeof code === 'number') {
addEnumEntry(name, code)
} else if (Array.isArray(code)) {
for (const c of code) {
if (typeof c === 'number') {
addEnumEntry(name, c)
}
}
}
}
})
}
Expand Down Expand Up @@ -166,6 +176,8 @@ export class EnumTag {
encodeToString(): string {
const values: string[] = []

// Please note, this doesn't encode everything. For instance,
// enumerations that share a common index.
for (const [name, code] of this.#nameToCodeMap) {
values[code] = name
}
Expand All @@ -179,16 +191,20 @@ export class EnumTag {
encodeToGrid(): HGrid {
const rows: HDict[] = []

for (const name of this.#nameToCodeMap.keys()) {
const code = this.nameToCode(name)
const obj = this.encodeToObject()

if (code !== undefined) {
rows.push(
new HDict({
name,
code,
})
)
for (const name of Object.keys(obj)) {
const codes = obj[name]

if (codes) {
for (const code of codes) {
rows.push(
new HDict({
name,
code,
})
)
}
}
}

Expand All @@ -198,14 +214,32 @@ export class EnumTag {
/**
* @returns encodes the enumerations to an object.
*/
encodeToObject(): Record<string, number> {
const obj: Record<string, number> = {}
encodeToObject(): Record<string, number[]> {
const obj: Record<string, number[]> = {}

// Try name to code first.
for (const name of this.#nameToCodeMap.keys()) {
const code = this.nameToCode(name)

if (code !== undefined) {
obj[name] = code
obj[name] = [code]
}
}

// Try codes to name so we have everything.
for (const code of this.#codeToNameMap.keys()) {
const name = this.codeToName(code)

if (name !== undefined) {
let codes = obj[name]

if (!codes) {
obj[name] = codes = []
}

if (!codes.includes(code)) {
codes.push(code)
}
}
}

Expand Down

0 comments on commit 1003926

Please sign in to comment.