Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEATURE] Added new method betweenLexoRanks which allows to generate multiple between lexoRank based on user input #1

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ const any1LexoRank = LexoRank.min();
const any2LexoRank = any1LexoRank.genNext().genNext();
// calculate between
const betweenLexoRank = any1LexoRank.between(any2LexoRank);
// calculate between lexoRanks
const betweenLexoRanks = any1LexoRank.multipleBetween(any2LexoRank, 5);
```

## Related projects
Expand Down
107 changes: 107 additions & 0 deletions __tests__/lexoRank.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,111 @@ describe('LexoRank', () => {
const between = prevRank.between(nextRank);
expect(between.toString()).toEqual(expected);
});

it('find the nearest power', () => {
expect(LexoRank.binaryDepthToInsertRanks(1)).toEqual(1);
expect(LexoRank.binaryDepthToInsertRanks(2)).toEqual(2);
expect(LexoRank.binaryDepthToInsertRanks(3)).toEqual(2);
expect(LexoRank.binaryDepthToInsertRanks(4)).toEqual(3);
expect(LexoRank.binaryDepthToInsertRanks(5)).toEqual(3);
expect(LexoRank.binaryDepthToInsertRanks(6)).toEqual(3);
expect(LexoRank.binaryDepthToInsertRanks(7)).toEqual(3);
expect(LexoRank.binaryDepthToInsertRanks(8)).toEqual(4);
expect(LexoRank.binaryDepthToInsertRanks(15)).toEqual(4);
expect(LexoRank.binaryDepthToInsertRanks(31)).toEqual(5);
expect(LexoRank.binaryDepthToInsertRanks(63)).toEqual(6);
expect(LexoRank.binaryDepthToInsertRanks(124)).toEqual(7);
expect(LexoRank.binaryDepthToInsertRanks(127)).toEqual(7);
});

it('find the multipleBetween for 1 lexoRanks to generate', () => {
const minRank = LexoRank.min();
const maxRank = LexoRank.max();
const lexoRanks: LexoRank[] = minRank.multipleBetween(maxRank, 1);
expect(lexoRanks.length).toEqual(1);
expect(lexoRanks[0].toString()).toEqual('0|hzzzzz:');
});

it('find the multipleBetween for 2 lexoRanks to generate', () => {
const minRank = LexoRank.min();
const maxRank = LexoRank.max();
const lexoRanks: LexoRank[] = minRank.multipleBetween(maxRank, 2);
expect(lexoRanks.length).toEqual(2);
expect(lexoRanks[0].toString()).toEqual('0|8zzzzz:');
expect(lexoRanks[1].toString()).toEqual('0|hzzzzz:');
expect(checkSortedInAscendingOrder(lexoRanks)).toEqual(true);
});

it('find the multipleBetween for 2 lexoRanks when one lexoRank is long in length', () => {
const maxRank = LexoRank.parse('0|i00006:zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzr');
const minRank = LexoRank.parse('0|i00006:zzr');
const lexoRanks: LexoRank[] = minRank.multipleBetween(maxRank, 2);
expect(lexoRanks.length).toEqual(2);
expect(lexoRanks[0].toString()).toEqual('0|i00006:zzt');
expect(lexoRanks[1].toString()).toEqual('0|i00006:zzv');
expect(checkSortedInAscendingOrder(lexoRanks)).toEqual(true);
});

it('find the multipleBetween for 3 lexoRanks to generate', () => {
const minRank = LexoRank.min();
const maxRank = LexoRank.max();
const lexoRanks: LexoRank[] = minRank.multipleBetween(maxRank, 3);
expect(lexoRanks.length).toEqual(3);
expect(lexoRanks[0].toString()).toEqual('0|8zzzzz:');
expect(lexoRanks[1].toString()).toEqual('0|hzzzzz:');
expect(lexoRanks[2].toString()).toEqual('0|qzzzzz:');
expect(checkSortedInAscendingOrder(lexoRanks)).toEqual(true);
});

it('find the multipleBetween for 4 lexoRanks to generate', () => {
const minRank = LexoRank.min();
const maxRank = LexoRank.max();
const lexoRanks: LexoRank[] = minRank.multipleBetween(maxRank, 4);
expect(lexoRanks.length).toEqual(4);
expect(lexoRanks[0].toString()).toEqual('0|4hzzzz:');
expect(lexoRanks[1].toString()).toEqual('0|8zzzzz:');
expect(lexoRanks[2].toString()).toEqual('0|dhzzzz:');
expect(lexoRanks[3].toString()).toEqual('0|hzzzzz:');
expect(checkSortedInAscendingOrder(lexoRanks)).toEqual(true);
});

it('find the multipleBetween for 7 lexoRanks to generate', () => {
const minRank = LexoRank.min();
const maxRank = LexoRank.max();
const lexoRanks: LexoRank[] = minRank.multipleBetween(maxRank, 7);
expect(lexoRanks.length).toEqual(7);
expect(lexoRanks[0].toString()).toEqual('0|4hzzzz:');
expect(lexoRanks[1].toString()).toEqual('0|8zzzzz:');
expect(lexoRanks[2].toString()).toEqual('0|dhzzzz:');
expect(lexoRanks[3].toString()).toEqual('0|hzzzzz:');
expect(lexoRanks[4].toString()).toEqual('0|mhzzzz:');
expect(lexoRanks[5].toString()).toEqual('0|qzzzzz:');
expect(lexoRanks[6].toString()).toEqual('0|vhzzzz:');
expect(checkSortedInAscendingOrder(lexoRanks)).toEqual(true);
});

it('find the multipleBetween for 8 lexoRanks when one lexoRank is long in length', () => {
const maxRank = LexoRank.parse('0|i00006:zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzr');
const minRank = LexoRank.parse('0|i00006:zzr');
const lexoRanks: LexoRank[] = minRank.multipleBetween(maxRank, 8);
expect(lexoRanks.length).toEqual(8);
expect(lexoRanks[0].toString()).toEqual('0|i00006:zzri');
expect(lexoRanks[1].toString()).toEqual('0|i00006:zzs');
expect(lexoRanks[2].toString()).toEqual('0|i00006:zzsi');
expect(lexoRanks[3].toString()).toEqual('0|i00006:zzt');
expect(lexoRanks[4].toString()).toEqual('0|i00006:zzti');
expect(lexoRanks[5].toString()).toEqual('0|i00006:zzu');
expect(lexoRanks[6].toString()).toEqual('0|i00006:zzui');
expect(lexoRanks[7].toString()).toEqual('0|i00006:zzv');
expect(checkSortedInAscendingOrder(lexoRanks)).toEqual(true);
});

function checkSortedInAscendingOrder(arr) {
for (let i = 0; i < arr.length - 1; i++) {
if (arr[i] > arr[i + 1]) {
return false;
}
}
return true;
}
});
76 changes: 76 additions & 0 deletions src/lexoRank/lexoRank.ts
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,69 @@ export class LexoRank {
return new LexoRank(this.bucket, LexoRank.between(this.decimal, other.decimal));
}

public multipleBetween(other: LexoRank, ranksToGenerate: number): LexoRank[] {

if (!this.bucket.equals(other.bucket)) {
throw new Error('Between works only within the same bucket');
}

if (ranksToGenerate <= 0) {
throw new Error(`'ranksToGenerate' should be greater than 0`);
}

if (ranksToGenerate == 1) {
return [this.between(other)];
}

let left: LexoRank = this;
let right: LexoRank = other;

const cmp = this.decimal.compareTo(other.decimal);
if (cmp > 0) {
left = other;
right = this;
}

if (cmp === 0) {
throw new Error(
'Try to rank between issues with same rank this=' +
this +
' other=' +
other +
' this.decimal=' +
this.decimal +
' other.decimal=' +
other.decimal
);
}

const binaryDepth: number = LexoRank.binaryDepthToInsertRanks(ranksToGenerate);

const lexoRanks: LexoRank[] = [];

LexoRank.prepareLexoRanks(left, right, 1, binaryDepth, lexoRanks);

return lexoRanks.slice(0, ranksToGenerate);
}

private static prepareLexoRanks(left: LexoRank, right: LexoRank, currentDepth: number, maxDepth: number, lexoRanks: LexoRank[]) {
// find the midpoint for this operation
const rankAtThisLevel: LexoRank = left.between(right);

if (currentDepth < maxDepth) {
// recursive into the left subtree
LexoRank.prepareLexoRanks(left, rankAtThisLevel, currentDepth + 1, maxDepth, lexoRanks);
}

// insert the LexoRank at this level
lexoRanks.push(rankAtThisLevel);

if (currentDepth < maxDepth) {
// recursive into the right subtree
LexoRank.prepareLexoRanks(rankAtThisLevel, right, currentDepth + 1, maxDepth, lexoRanks);
}
}

public getBucket(): LexoRankBucket {
return this.bucket;
}
Expand Down Expand Up @@ -366,4 +429,17 @@ export class LexoRank {

return this.value.localeCompare(other.value);
}

// Visible for unit test
public static binaryDepthToInsertRanks(noOfRanks: number): number {
if (noOfRanks < 1) return 0;

let power = 1;
while (Math.pow(2, power) - 1 < noOfRanks) {
power++;
}

return power;
}

}