All files / last-grapheme-cluster/lib main.js

58.09% Statements 61/105
100% Branches 1/1
0% Functions 0/1
58.09% Lines 61/105

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 1061x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x                                                                                         1x 1x 1x 1x 1x  
/**
* @license Apache-2.0
*
* Copyright (c) 2024 The Stdlib Authors.
*
* Licensed 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.
*/
 
'use strict';
 
// MODULES //
 
var nextGraphemeClusterBreak = require( '@stdlib/string/next-grapheme-cluster-break' );
var CircularBuffer = require( '@stdlib/dstructs/circular-buffer' );
var zeros = require( '@stdlib/array/base/zeros' );
 
 
// MAIN //
 
/**
* Returns the last `n` grapheme clusters (i.e., user-perceived characters) of a string.
*
* @param {string} str - input string
* @param {NonNegativeInteger} n - number of grapheme clusters to return
* @returns {string} output string
*
* @example
* var out = last( 'Hello World', 1 );
* // returns 'd'
*
* @example
* var out = last( 'Evening', 3 );
* // returns 'ing'
*
* @example
* var out = last( 'JavaScript', 6 );
* // returns 'Script'
*
* @example
* var out = last( '六书/六書', 1 );
* // returns '書'
*
* @example
* var out = last( '🐶🐮🐷🐰🐸', 2 );
* // returns '🐰🐸'
*/
function last( str, n ) {
	var count;
	var cbuf;
	var buf;
	var i;

	if ( n === 0 || str === '' ) {
		return '';
	}
	// Resolve the first cluster break:
	i = nextGraphemeClusterBreak( str, 0 );

	// If we received a sentinel value, return the input string, as there are no more cluster breaks to iterate over...
	if ( i === -1 ) {
		return str;
	}
	// Initialize a buffer for keeping track of cluster break indices:
	buf = zeros( n );

	// Wrap the buffer to create a circular buffer serving as a FIFO stack where we can keep at most `n` indices as we iterate from left-to-right:
	cbuf = new CircularBuffer( buf );

	// Add the first character index:
	cbuf.push( 0 );

	// Add the index of the first grapheme cluster break to our buffer:
	cbuf.push( i );

	// Slide a window over the string from left-to-right...
	count = 0;
	while ( true ) {
		count += 1;
		i = nextGraphemeClusterBreak( str, i );
		if ( i === -1 ) {
			break;
		}
		cbuf.push( i );
	}
	// Resolve the leftmost index:
	i = buf[ (count+1)%n ]; // count+1 as count%n corresponds to the index of the "newest" element in the circular buffer and count+1 is the next element to replace (i.e., the "oldest" index)

	// Return the last `n` grapheme clusters:
	return str.substring( i );
}
 
 
// EXPORTS //
 
module.exports = last;