# Sparkline in unicode

Sparkline in unicode
You are encouraged to solve this task according to the task description, using any language you may know.

A sparkline is a graph of successive values laid out horizontally where the height of the line is proportional to the values in succession.

Task

Use the following series of Unicode characters to create a program that takes a series of numbers separated by one or more whitespace or comma characters and generates a sparkline-type bar graph of the values on a single line of output.

The eight characters: '▁▂▃▄▅▆▇█'
(Unicode values U+2581 through U+2588).

Use your program to show sparklines for the following input, here on this page:

1. 1 2 3 4 5 6 7 8 7 6 5 4 3 2 1
2. 1.5, 0.5 3.5, 2.5 5.5, 4.5 7.5, 6.5
(note the mix of separators in this second case)!
Notes
• A space is not part of the generated sparkline.
• The sparkline may be accompanied by simple statistics of the data such as its range.

## APL

Note → this is in a 0-indexed version of APL

Solution:
`   sparkln←{'▁▂▃▄▅▆▇█'[⌊0.5+7×⍵÷⌈/⍵]}`
Example:
`      sparkln 1 2 3 4 5 6 7 8 7 6 5 4 3 2 1▂▃▄▅▅▆▇█▇▆▅▅▄▃▂      sparkln 1.5, 0.5 3.5, 2.5 5.5, 4.5 7.5, 6.5▂▁▄▃▆▅█▇ `

Note → APL accepts the input with commas and spaces naturally. If one wanted to read input as a string they could use ⍎⍞ to do so.

## AppleScript

`use framework "Foundation" -- Yosemite onwards – for splitting by regex -- SPARKLINE ----------------------------------------------------------------- -- sparkLine :: [Num] -> Stringon sparkLine(xs)    set min to minimumBy(my numericOrdering, xs)    set max to maximumBy(my numericOrdering, xs)    set dataRange to max - min     -- scale :: Num -> Num    script scale        on |λ|(x)            ((x - min) * 7) / dataRange        end |λ|    end script     -- bucket :: Num -> String    script bucket        on |λ|(n)            if n ≥ 0 and n < 8 then                item (n + 1 as integer) of "▁▂▃▄▅▆▇█"            else                missing value            end if        end |λ|    end script     intercalate("", map(bucket, map(scale, xs)))end sparkLine -- numericOrdering :: Num -> Num -> (-1 | 0 | 1)on numericOrdering(a, b)    if a < b then        -1    else        if a > b then            1        else            0        end if    end ifend numericOrdering  -- TEST ----------------------------------------------------------------------on run     -- splitNumbers :: String -> [Real]    script splitNumbers        script asReal            on |λ|(x)                x as real            end |λ|        end script         on |λ|(s)            map(asReal, splitRegex("[\\s,]+", s))        end |λ|    end script     map(sparkLine, map(splitNumbers, ["1 2 3 4 5 6 7 8 7 6 5 4 3 2 1", ¬        "1.5, 0.5 3.5, 2.5 5.5, 4.5 7.5, 6.5", ¬        "3 2 1 0 -1 -2 -3 -4 -3 -2 -1 0 1 2 3", ¬        "-1000 100 1000 500 200 -400 -700 621 -189 3"]))     -- {"▁▂▃▄▅▆▇█▇▆▅▄▃▂▁","▂▁▄▃▆▅█▇","█▇▆▅▄▃▂▁▂▃▄▅▆▇█","▁▅█▆▅▃▂▇▄▅"}end run  -- GENERIC LIBRARY FUNCTIONS ------------------------------------------------- -- foldl :: (a -> b -> a) -> a -> [b] -> aon foldl(f, startValue, xs)    tell mReturn(f)        set v to startValue        set lng to length of xs        repeat with i from 1 to lng            set v to |λ|(v, item i of xs, i, xs)        end repeat        return v    end tellend foldl -- intercalate :: Text -> [Text] -> Texton intercalate(strText, lstText)    set {dlm, my text item delimiters} to {my text item delimiters, strText}    set strJoined to lstText as text    set my text item delimiters to dlm    return strJoinedend intercalate -- map :: (a -> b) -> [a] -> [b]on map(f, xs)    tell mReturn(f)        set lng to length of xs        set lst to {}        repeat with i from 1 to lng            set end of lst to |λ|(item i of xs, i, xs)        end repeat        return lst    end tellend map -- maximumBy :: (a -> a -> Ordering) -> [a] -> a on maximumBy(f, xs)    script max        property cmp : f        on |λ|(a, b)            if a is missing value or cmp(a, b) < 0 then                b            else                a            end if        end |λ|    end script     foldl(max, missing value, xs)end maximumBy -- minimumBy :: (a -> a -> Ordering) -> [a] -> a on minimumBy(f, xs)    script min        property cmp : f        on |λ|(a, b)            if a is missing value or cmp(a, b) > 0 then                b            else                a            end if        end |λ|    end script     foldl(min, missing value, xs)end minimumBy -- Lift 2nd class handler function into 1st class script wrapper -- mReturn :: Handler -> Scripton mReturn(f)    if class of f is script then        f    else        script            property |λ| : f        end script    end ifend mReturn -- regexMatches :: RegexPattern -> String -> [{location:Int, length:Int}]on regexMatches(strRegex, str)    set ca to current application    set oRgx to ca's NSRegularExpression's regularExpressionWithPattern:strRegex ¬        options:((ca's NSRegularExpressionAnchorsMatchLines as integer)) |error|:(missing value)    set oString to ca's NSString's stringWithString:str    set oMatches to oRgx's matchesInString:oString options:0 range:{location:0, |length|:oString's |length|()}     set lstMatches to {}    set lng to count of oMatches    repeat with i from 1 to lng        set end of lstMatches to range() of item i of oMatches    end repeat    lstMatchesend regexMatches -- splitRegex :: RegexPattern -> String -> [String]on splitRegex(strRegex, str)    set lstMatches to regexMatches(strRegex, str)    if length of lstMatches > 0 then        script preceding            on |λ|(a, x)                set iFrom to start of a                set iLocn to (location of x)                 if iLocn > iFrom then                    set strPart to text (iFrom + 1) thru iLocn of str                else                    set strPart to ""                end if                {parts:parts of a & strPart, start:iLocn + (length of x) - 1}            end |λ|        end script         set recLast to foldl(preceding, {parts:[], start:0}, lstMatches)         set iFinal to start of recLast        if iFinal < length of str then            parts of recLast & text (iFinal + 1) thru -1 of str        else            parts of recLast & ""        end if    else        {str}    end ifend splitRegex`
`{"▁▂▃▄▅▆▇█▇▆▅▄▃▂▁","▂▁▄▃▆▅█▇","█▇▆▅▄▃▂▁▂▃▄▅▆▇█","▁▅█▆▅▃▂▇▄▅"}`

## AutoHotkey

Works with: AutoHotkey_L
`SetFormat, FloatFast, 0.1strings := ["1 2 3 4 5 6 7 8 7 6 5 4 3 2 1"         ,  "1.5, 0.5 3.5, 2.5 5.5, 4.5 7.5, 6.5"] Loop, % strings.MaxIndex(){    SL := Sparklines(strings[A_Index])    MsgBox, % "Min: " SL["Min"] ", Max: " SL["Max"] ", Range: " SL["Rng"] "`n" SL["Chars"]} Sparklines(s){    s := RegexReplace(s, "[^\d\.]+", ",")    Loop, Parse, s, `,    {        Max := A_LoopField > Max ? A_LoopField : Max        Min := !Min ? Max : A_LoopField < Min ? A_LoopField : Min    }    Rng := Max - Min    Loop, Parse, s, `,        Chars .= Chr(0x2581 + Round(7 * (A_LoopField - Min) / Rng))    return, {"Min": Min, "Max": Max, "Rng": Rng, "Chars": Chars}}`
Output:
```Min: 1, Max: 8, Range: 7
▁▂▃▄▅▆▇█▇▆▅▄▃▂▁

Min: 0.5, Max: 7.5, Range: 7.0
▂▁▄▃▆▅█▇```

## C

This seemingly simple task turns out to be very complicated for languages like C. As the characters to be printed are Unicode, there is no platform independent way to do so. The implementation below works on Linux but not on Windows, detailed investigations show that there is no way, at least no reliable and/or easily reproducible way, to accomplish this task via C on Windows. This is not due to any lacunae in C but due to the vagaries of the Windows Command shell.

### Linux version

Accepts data via command line, prints out usage on incorrect invocation.

` #include<string.h>#include<stdlib.h>#include<locale.h>#include<stdio.h>#include<wchar.h>#include<math.h> int main(int argC,char* argV[]){	double* arr,min,max;	char* str;	int i,len;	if(argC == 1)		printf("Usage : %s <data points separated by spaces or commas>",argV[0]);	else{		arr = (double*)malloc((argC-1)*sizeof(double));		for(i=1;i<argC;i++){			len = strlen(argV[i]); 			if(argV[i][len-1]==','){				str = (char*)malloc(len*sizeof(char));				strncpy(str,argV[i],len-1);				arr[i-1] = atof(str);				free(str);			}			else				arr[i-1] = atof(argV[i]);			if(i==1){				min = arr[i-1];				max = arr[i-1];			}			else{				min=(min<arr[i-1]?min:arr[i-1]);				max=(max>arr[i-1]?max:arr[i-1]);			}		} 		printf("\n%Max : %lf,Min : %lf,Range : %lf\n",max,min,max-min); 		setlocale(LC_ALL, ""); 		for(i=1;i<argC;i++){			printf("%lc", (wint_t)(9601 + (int)ceil((arr[i-1]-min)/(max-min)*7)));		}	}	return 0;} `

Invocation and output :

```/home/aamrun/rosettaCode>./sparkLine 1 2 3 4 5 6 7 8 7 6 5 4 3 2 1
Max : 8.000000,Min : 1.000000,Range : 7.000000
▁▂▃▄▅▆▇█▇▆▅▄▃▂▁
/home/aamrun/rosettaCode>./sparkLine 1.5, 0.5 3.5, 2.5 5.5, 4.5 7.5, 6.5
Max : 7.500000,Min : 0.500000,Range : 7.000000
▂▁▄▃▆▅█▇
```

## C++

` #include <iostream>#include <sstream>#include <vector>#include <cmath>#include <algorithm>#include <locale> class Sparkline {    public:        Sparkline(std::wstring &cs) : charset( cs ){        }        virtual ~Sparkline(){        }         void print(std::string spark){            const char *delim = ", ";            std::vector<float> data;            // Get first non-delimiter            std::string::size_type last = spark.find_first_not_of(delim, 0);            // Get end of token            std::string::size_type pos = spark.find_first_of(delim, last);             while( pos != std::string::npos || last != std::string::npos ){                std::string tok = spark.substr(last, pos-last);                // Convert to float:                std::stringstream ss(tok);                float entry;                ss >> entry;                 data.push_back( entry );                 last = spark.find_first_not_of(delim, pos);                pos = spark.find_first_of(delim, last);            }             // Get range of dataset            float min = *std::min_element( data.begin(), data.end() );            float max = *std::max_element( data.begin(), data.end() );             float skip = (charset.length()-1) / (max - min);             std::wcout<<L"Min: "<<min<<L"; Max: "<<max<<L"; Range: "<<(max-min)<<std::endl;             std::vector<float>::const_iterator it;            for(it = data.begin(); it != data.end(); it++){                float v = ( (*it) - min ) * skip;                 std::wcout<<charset[ (int)floor( v ) ];            }            std::wcout<<std::endl;         }    private:        std::wstring &charset;}; int main( int argc, char **argv ){    std::wstring charset = L"\u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588";     // Mainly just set up utf-8, so wcout won't narrow our characters.    std::locale::global(std::locale("en_US.utf8"));     Sparkline sl(charset);     sl.print("1 2 3 4 5 6 7 8 7 6 5 4 3 2 1");    sl.print("1.5, 0.5 3.5, 2.5 5.5, 4.5 7.5, 6.5");     return 0;}`
Output:
```Min: 1; Max: 8; Range: 7
▁▂▃▄▅▆▇█▇▆▅▄▃▂▁
Min: 0.5; Max: 7.5; Range: 7
▂▁▄▃▆▅█▇```

## Clojure

`(defn sparkline [nums]  (let [sparks   "▁▂▃▄▅▆▇█"        high     (apply max nums)        low      (apply min nums)        spread   (- high low)        quantize #(Math/round (* 7.0 (/ (- % low) spread)))]        (apply str (map #(nth sparks (quantize %)) nums)))) (defn spark [line]  (if line    (let [nums (read-string (str "[" line "]"))]       (println (sparkline nums))      (recur (read-line))))) (spark (read-line))`
Output:
```\$ clj sparkline.clj <<<\$'1 2 3 4 5 6 7 8 7 6 5 4 3 2 1\n1.5, 0.5 3.5, 2.5 5.5, 4.5 7.5, 6.5'
▁▂▃▄▅▆▇█▇▆▅▄▃▂▁
▂▁▄▃▆▅█▇
```

## Common Lisp

`(defun buckets (numbers)  (loop with min = (apply #'min numbers)        with max = (apply #'max numbers)        with width = (/ (- max min) 7)        for base from (- min (/ width 2)) by width        repeat 8        collect (cons base (+ base width)))) (defun bucket-for-number (number buckets)  (loop for i from 0        for (min . max) in buckets        when (and (<= min number) (< number max))          return i)) (defun sparkline (numbers)  (loop with buckets = (buckets numbers)        with sparks = "▁▂▃▄▅▆▇█"        with sparkline = (make-array (length numbers) :element-type 'character)        with min = (apply #'min numbers)        with max = (apply #'max numbers)        for number in numbers        for i from 0        for bucket = (bucket-for-number number buckets)        do (setf (aref sparkline i) (char sparks bucket))        finally (format t "Min: ~A, Max: ~A, Range: ~A~%" min max (- max min))                (write-line sparkline))) (defun string->numbers (string)  (flet ((delimiterp (c)           (or (char= c #\Space) (char= c #\,))))    (loop for prev-end = 0 then end          while prev-end          for start = (position-if-not #'delimiterp string :start prev-end)          for end = (position-if #'delimiterp string :start start)          for number = (read-from-string string t nil :start start :end end)          do (assert (numberp number))          collect number))) (defun string->sparkline (string)  (sparkline (string->numbers string))) (string->sparkline "1 2 3 4 5 6 7 8 7 6 5 4 3 2 1")(string->sparkline "1.5, 0.5 3.5, 2.5 5.5, 4.5 7.5, 6.5")`
Output:
```Min: 1, Max: 8, Range: 7
▁▂▃▄▅▆▇█▇▆▅▄▃▂▁
Min: 0.5, Max: 7.5, Range: 7.0
▂▁▄▃▆▅█▇```

## D

Translation of: Python
`void main() {    import std.stdio, std.range, std.algorithm, std.conv,           std.string, std.regex;     "Numbers please separated by space/commas: ".write;    immutable numbers = readln                        .strip                        .splitter(r"[\s,]+".regex)                        .array /**/                        .to!(real[]);    immutable mm = numbers.reduce!(min, max);    writefln("min: %4f, max: %4f", mm[]);    immutable bars = iota(9601, 9609).map!(i => i.to!dchar).dtext;    immutable div = (mm[1] - mm[0]) / (bars.length - 1);    numbers.map!(n => bars[cast(int)((n - mm[0]) / div)]).writeln;}`

The output is the same as the Python entry (but it only accepts one series of values at a time).

## Elixir

`defmodule RC do  def sparkline(str) do    values = str |> String.split(~r/(,| )+/)                 |> Enum.map(&elem(Float.parse(&1), 0))    {min, max} = Enum.min_max(values)    IO.puts Enum.map(values, &(round((&1 - min) / (max - min) * 7 + 0x2581)))  endend`

Usage:

`str1 = "1 2 3 4 5 6 7 8 7 6 5 4 3 2 1"str2 = "1.5, 0.5 3.5, 2.5 5.5, 4.5 7.5, 6.5" RC.sparkline(str1)IO.puts "" # newlineRC.sparkline(str2)`
Output:
```▁▂▃▄▅▆▇█▇▆▅▄▃▂▁

▂▁▄▃▆▅█▇
```

## F#

`open Systemopen System.Globalizationopen System.Text.RegularExpressions let bars = Array.map Char.ToString ("▁▂▃▄▅▆▇█".ToCharArray()) while true do    printf "Numbers separated by anything: "    let numbers =        [for x in Regex.Matches(Console.ReadLine(), @"-?\d+(?:\.\d*)?") do yield x.Value]        |> List.map (fun x -> Double.Parse(x, CultureInfo.InvariantCulture))    if numbers.Length = 0 then System.Environment.Exit(0)    if numbers.Length = 1 then        printfn "A sparkline for 1 value is not very useful... ignoring entry"    else        let min, max = List.min numbers, List.max numbers        printfn "min: %5f; max: %5f" min max        let barsCount = float (bars.GetUpperBound(0))        numbers        |> List.map (fun x -> bars.[int ((x - min)/(max - min) * barsCount)])        |> String.Concat        |> printfn "%s"`
Output:
```Numbers separated by anything: 1 2 3 4 5 6 7 8 7 6 5 4 3 2 1
min: 1.000000; max: 8.000000
▁▂▃▄▅▆▇█▇▆▅▄▃▂▁
Numbers separated by anything: 1.5, 0.5 3.5, 2.5 5.5, 4.5 7.5, 6.5
min: 0.500000; max: 7.500000
▂▁▄▃▆▅█▇
Numbers separated by anything:
```

## FALSE

`{  variables:  s: sign (1 or -1)  u: current number  f: current number fraction length  v: current number is valid  t: number of numbers read  x: biggest fraction  y: smallest number (without fraction)  z: biggest number (without fraction)} {function a: test if top is 0-9, without popping the value, codes 48-57 are in range}[\$\$47>\57>~&]a: {function b: test if top is ',' or ' ', without popping the value}[\$\$',=\' =|]b: {function c: read a number from the input, given that the first character of the input is already on the stack}[  1s:0u:0f:0v: {reset values}   \$'-=[1_s:%^]? {if (it is negative) set the sign value to -1 move to next}  [a;!][48-u;10*+u:1_v:^]# {while (isnumber) do number = number * 10 + decimal and set valid number and move to next}  \$'.=[ {if (it is a decimal) move forward and read fraction}    %^    [a;!][48-u;10*+u:f;1+f:1_v:^]# {while (isnumber) do number = number * 10 + decimal and increase fraction length and set valid number and move to next}  ]?  \$\$'-=\'.=|[0v:]? {if next charachter is a '-' or a '.', set invalid}]c: {function d: normalize number/fraction from stack to max fraction and push that number}[  [\$x;=~][1+\10*\]# {while (fraction != max) fraction + 1, value * 10}  % {pop fraction}]d: 0t:0x:1_v: { nothing read, so we are still valid }^[b;!][%^]# {read away any initial separators}[\$1_=~v;&][ {while input != -1 and valid input, leaving input on the stack}  c;! {read a number}  t;1+t:u;s;*f;@ {increase count, push number * sign and fraction length onto the stack and bring input back up}  f;x;>[f;x:]? {set fraction to biggest of current and previous biggest}  [b;!][%^]# {while (isseparator) move forward}]#v;~["error at charachter ",]? {if invalid number, tell them when}v;[ {if last number also valid, do the math}  %        {pop the -1}  t;2*1-q: {var q: points to next value}  0p:      {var p: whether min/max have been set}  [q;1+t;>][ {while q + 1 > t}    q;ø        {current number}    q;ø        {current fraction}    d;!        {normalize}    p;[\$y;\>[\$y:]? \$z;>[\$z:]?]?      {compare min/max}    p;~[1_p:\$y:\$z:]?     {if (first)) set min/max}    q;1-q:     {move pointer}  ]#   t;q: {point q to first value}  [q;0>][ {while q > 0}    q;1-øy;-7*z;y;-/ {(number - minvalue) * 7 / (maxvalue - minvalue), should result in 0..7}    9601+,           {print character}    q;1-q:           {move pointer}  ]#]?`

This implementation can only accept one series of numbers at a time.

Output:
```▁▂▃▄▅▆▇█▇▆▅▄▃▂▁
▂▁▄▃▆▅█▇
```

## Go

`package main import (    "bufio"    "errors"    "fmt"    "math"    "os"    "regexp"    "strconv"    "strings") func main() {    fmt.Println("Numbers please separated by space/commas:")    sc := bufio.NewScanner(os.Stdin)    sc.Scan()    s, n, min, max, err := spark(sc.Text())    if err != nil {        fmt.Println(err)        return    }    if n == 1 {        fmt.Println("1 value =", min)    } else {        fmt.Println(n, "values.  Min:", min, "Max:", max)    }    fmt.Println(s)} var sep = regexp.MustCompile(`[\s,]+`) func spark(s0 string) (sp string, n int, min, max float64, err error) {    ss := sep.Split(s0, -1)    n = len(ss)    vs := make([]float64, n)    var v float64    min = math.Inf(1)    max = math.Inf(-1)    for i, s := range ss {        switch v, err = strconv.ParseFloat(s, 64); {        case err != nil:        case math.IsNaN(v):            err = errors.New("NaN not supported.")        case math.IsInf(v, 0):            err = errors.New("Inf not supported.")        default:            if v < min {                min = v            }            if v > max {                max = v            }            vs[i] = v            continue        }        return    }    if min == max {        sp = strings.Repeat("▄", n)    } else {        rs := make([]rune, n)        f := 8 / (max - min)        for j, v := range vs {            i := rune(f * (v - min))            if i > 7 {                i = 7            }            rs[j] = '▁' + i        }        sp = string(rs)    }    return}`
Output:
```Numbers please separated by space/commas:
1 2 3 4 5 6 7 8 7 6 5 4 3 2 1
15 values.  Min: 1 Max: 8
▁▂▃▄▅▆▇█▇▆▅▄▃▂▁
Numbers please separated by space/commas:
1.5, 0.5 3.5, 2.5 5.5, 4.5 7.5, 6.5
8 values.  Min: 0.5 Max: 7.5
▂▁▄▃▆▅█▇

Numbers please separated by space/commas:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
24 values.  Min: 1 Max: 24
▁▁▁▂▂▂▃▃▃▄▄▄▅▅▅▆▆▆▇▇▇███
Numbers please separated by space/commas:
0 99 101 699 701 800
6 values.  Min: 0 Max: 800
▁▁▂▇██
Numbers please separated by space/commas:
0 -.09 -.11 -.69 -.71 -.8
6 values.  Min: -0.8 Max: 0
██▇▂▁▁
Numbers please separated by space/commas:
3 3 3
3 values.  Min: 3 Max: 3
▄▄▄
Numbers please separated by space/commas:
1e99
1 value = 1e+99
▄
Numbers please separated by space/commas:

strconv.ParseFloat: parsing "": invalid syntax
```

## Groovy

`def sparkline(List<Number> list) {    def (min, max) = [list.min(), list.max()]    def div = (max - min) / 7    list.collect { (char)(0x2581 + (it-min) * div) }.join()}def sparkline(String text) { sparkline(text.split(/[ ,]+/).collect { it as Double }) }`

Test Code

`["1 2 3 4 5 6 7 8 7 6 5 4 3 2 1", "1.5, 0.5 3.5, 2.5 5.5, 4.5 7.5, 6.5"].each { dataset ->    println "  Dataset: \$dataset"    println "Sparkline: \${sparkline(dataset)}"}`
Output:
```  Dataset: 1 2 3 4 5 6 7 8 7 6 5 4 3 2 1
Sparkline: ▁▂▃▄▅▆▇█▇▆▅▄▃▂▁
Dataset: 1.5, 0.5 3.5, 2.5 5.5, 4.5 7.5, 6.5
Sparkline: ▂▁▄▃▆▅█▇
```

## Haskell

`import Data.Char (chr)import Data.List.Split (splitOneOf) toSparkLine :: [Double] -> [Char]toSparkLine xs = map cl xs    where        top = maximum xs        bot = minimum xs        range = top - bot        cl x = chr \$ 0x2581 + round ((x - bot) / range * 7) makeSparkLine :: String -> (String, Stats)makeSparkLine xs = (toSparkLine parsed, stats parsed)    where parsed = map read \$ filter (not . null) \$ splitOneOf " ," xs data Stats = Stats { minValue, maxValue, rangeOfValues :: Double,    numberOfValues :: Int } instance Show Stats where    show (Stats mn mx r n) = "min: " ++ show mn ++ "; max: " ++ show mx ++        "; range: " ++ show r ++ "; no. of values: " ++ show n stats :: [Double] -> Statsstats xs = Stats { minValue = mn, maxValue = mx,    rangeOfValues = mx - mn, numberOfValues = length xs }    where        mn = minimum xs        mx = maximum xs drawSparkLineWithStats :: String -> IO ()drawSparkLineWithStats xs = putStrLn sp >> print st    where (sp, st) = makeSparkLine xs main :: IO ()main = mapM_ drawSparkLineWithStats    ["1 2 3 4 5 6 7 8 7 6 5 4 3 2 1",    "1.5, 0.5 3.5, 2.5 5.5, 4.5 7.5, 6.5",    "3 2 1 0 -1 -2 -3 -4 -3 -2 -1 0 1 2 3",    "-1000 100 1000 500 200 -400 -700 621 -189 3"] `
Output:
```▁▂▃▄▅▆▇█▇▆▅▄▃▂▁
min: 1.0; max: 8.0; range: 7.0; no. of values: 15
▂▁▄▃▆▅█▇
min: 0.5; max: 7.5; range: 7.0; no. of values: 8
█▇▆▅▄▃▂▁▂▃▄▅▆▇█
min: -4.0; max: 3.0; range: 7.0; no. of values: 15
▁▅█▆▅▃▂▇▄▅
min: -1000.0; max: 1000.0; range: 2000.0; no. of values: 10```

Or, stripping back to the basics:

Translation of: JavaScript
`import Data.List.Split (splitOneOf) sparkLine :: [Float] -> StringsparkLine xs =  (("▁▂▃▄▅▆▇█" !!) . floor . (/ range) . (7 *) . subtract min) <\$> xs  where    min = minimum xs    range = maximum xs - min parseFloats :: String -> [Float]parseFloats = (read <\$>) . filter (not . null) . splitOneOf " ," main :: IO ()main =  mapM_    putStrLn    ((sparkLine . parseFloats) <\$>     [ "1 2 3 4 5 6 7 8 7 6 5 4 3 2 1"     , "1.5, 0.5 3.5, 2.5 5.5, 4.5 7.5, 6.5"     , "3 2 1 0 -1 -2 -3 -4 -3 -2 -1 0 1 2 3"     , "-1000 100 1000 500 200 -400 -700 621 -189 3"     ])`
Output:
```▁▂▃▄▅▆▇█▇▆▅▄▃▂▁
▂▁▄▃▆▅█▇
█▇▆▅▄▃▂▁▂▃▄▅▆▇█
▁▄█▆▅▃▂▆▃▄```

## J

Solution (explicit):
`   spkln =: verb define   	y spkln~ 4 u:16b2581+i.8  NB.  ▁▂▃▄▅▆▇█   :   	'MIN MAX' =. (<./ , >./) y   	N         =. # x   	x {~ <. (N-1) * (y-MIN) % MAX-MIN   )`
Solution (tacit):
`   spkln =: (4 u:16b2581+i.8)&\$: : ([ {~ <:@#@[ <[email protected]* ] (- % >./@[ - ]) <./@])`
Solution (look Ma, no hands!):
`   spkln =: (u:9601+i.8)&\$: : ([ {~ ((<[email protected]* <:@#)~ ((- % (- >./))~ <./)))`
Examples:
```   spkln 1 2 3 4 5 6 7 8 7 6 5 4 3 2 1
▁▂▃▄▅▆▇█▇▆▅▄▃▂▁
spkln 1.5, 0.5 3.5, 2.5 5.5, 4.5 7.5, 6.5
▁▅█▆▅▃▂▇▄▅```

Notes: J's grammar automatically normalizes numeric vector inputs which use a mix of whitespace and commas. If we wanted to normalize input ourselves, i.e. take string rather than numeric input (e.g. to make apples-for-apples comparisons with the languages easier) we could simply use ". ("eval") as a preprocessor, as in [email protected]". (which is simple because it's still taking advantage of J's grammar to normalize numeric vectors).

Note also: the font my browser uses to render these sparkline characters looks awful. Other fonts look better.

## Java

` public class Sparkline {	String bars="▁▂▃▄▅▆▇█";	public static void main(String[] args)	{		Sparkline now=new Sparkline();		float[] arr={1, 2, 3, 4, 5, 6, 7, 8, 7, 6, 5, 4, 3, 2, 1};		now.display1D(arr);		System.out.println(now.getSparkline(arr));		float[] arr1={1.5f, 0.5f, 3.5f, 2.5f, 5.5f, 4.5f, 7.5f, 6.5f};		now.display1D(arr1);		System.out.println(now.getSparkline(arr1));	}	public void display1D(float[] arr)	{		for(int i=0;i<arr.length;i++)			System.out.print(arr[i]+" ");		System.out.println();	}	public String getSparkline(float[] arr)	{		float min=Integer.MAX_VALUE;		float max=Integer.MIN_VALUE;		for(int i=0;i<arr.length;i++)		{			if(arr[i]<min)				min=arr[i];			if(arr[i]>max)				max=arr[i];		}		float range=max-min;		int num=bars.length()-1;		String line="";		for(int i=0;i<arr.length;i++)		{ 			line+=bars.charAt((int)Math.ceil(((arr[i]-min)/range*num)));		}		return line;	}} `
Output:
```1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 7.0 6.0 5.0 4.0 3.0 2.0 1.0
▁▂▃▄▅▆▇█▇▆▅▄▃▂▁
1.5 0.5 3.5 2.5 5.5 4.5 7.5 6.5
▂▁▄▃▆▅█▇

```

## JavaScript

### ES6

`(() => {    'use strict';     // sparkLine :: [Num] -> String    let sparkLine = xs => {            let min = minimumBy(numericOrdering, xs),                max = maximumBy(numericOrdering, xs),                range = max - min;             return xs.map(x => ((x - min) * 7) / range)                .map(                    n => (n >= 0 && n < 8) ? '▁▂▃▄▅▆▇█'                    .split('')[Math.round(n)] : undefined                ).join('');        },         // maximumBy :: (a -> a -> Ordering) -> [a] -> a        maximumBy = (f, xs) =>            xs.reduce((a, x) =>                a === undefined ? x : (                    f(x, a) > 0 ? x : a                ),                undefined            ),          // minimumBy :: (a -> a -> Ordering) -> [a] -> a        minimumBy = (f, xs) =>            xs.reduce((a, x) =>                a === undefined ? x : (                    f(x, a) < 0 ? x : a                ),                undefined            ),         numericOrdering = (a, b) => a < b ? -1 : (a > b ? 1 : 0);     // TEST     return ["1 2 3 4 5 6 7 8 7 6 5 4 3 2 1",        "1.5, 0.5 3.5, 2.5 5.5, 4.5 7.5, 6.5",        "3 2 1 0 -1 -2 -3 -4 -3 -2 -1 0 1 2 3",        "-1000 100 1000 500 200 -400 -700 621 -189 3"    ].map(        s => s.split(/[,\s]+/)        .map(x => parseFloat(x, 10))    ).map(sparkLine); })();`

`["▁▂▃▄▅▆▇█▇▆▅▄▃▂▁","▂▁▄▃▆▅█▇","█▇▆▅▄▃▂▁▂▃▄▅▆▇█","▁▅█▆▅▃▂▇▄▅"]`

## jq

`def sparkline:  min as \$min  | ( (max - \$min) / 7 ) as \$div  | map( 9601 +  (. - \$min) * \$div )  | implode ; def string2array:  def tidy: select( length > 0 );  [split(" ") | .[] | split(",") | .[] | tidy | tonumber];`

Task

```( "1 2 3 4 5 6 7 8 7 6 5 4 3 2 1",
"1.5, 0.5 3.5, 2.5 5.5, 4.5 7.5, 6.5"
)  | string2array | sparkline
```
Output:
```\$ jq -n -f -r sparkline.jq
▁▂▃▄▅▆▇█▇▆▅▄▃▂▁
▂▁▄▃▆▅█▇
```

## Julia

Works with: Julia version 0.6
`function sparklineit(arr::Vector{<:Integer})    sparkchars = '\u2581':'\u2588'    dyn = length(sparkchars)    lo, hi = extrema(arr)    b = @. max(ceil(Int, dyn * (arr - lo) / (hi - lo)), 1)    return join(sparkchars[b])end v = rand(0:10, 10)println("\$v → ", sparklineit(v))v = 10rand(10)println("\$(round.(v, 2)) → ", sparklineit(v))`
Output:
```[6, 3, 9, 5, 1, 10, 0, 1, 3, 6] → ▅▃█▄▁█▁▁▃▅
[0.57, 0.14, 4.73, 6.61, 6.9, 0.8, 9.71, 7.39, 2.75, 5.7] → ▁▁▄▆▆▁█▇▃▅```

## Kotlin

Translation of: Java
`internal const val bars = "▁▂▃▄▅▆▇█"internal const val n = bars.length - 1 fun <T: Number> Iterable<T>.toSparkline(): String {    var min = Double.MAX_VALUE    var max = Double.MIN_VALUE    val doubles = map { it.toDouble() }    doubles.forEach { i -> when { i < min -> min = i; i > max -> max = i } }    val range = max - min    return doubles.fold("") { line, d -> line + bars[Math.ceil((d - min) / range * n).toInt()] }} fun String.toSparkline() = replace(",", "").split(" ").map { it.toFloat() }.toSparkline() fun main(args: Array<String>) {    val s1 = "1 2 3 4 5 6 7 8 7 6 5 4 3 2 1"    println(s1)    println(s1.toSparkline())    val s2 = "1.5, 0.5 3.5, 2.5 5.5, 4.5 7.5, 6.5"    println(s2)    println(s2.toSparkline())}`
Output:
```1 2 3 4 5 6 7 8 7 6 5 4 3 2 1
▁▂▃▄▅▆▇█▇▆▅▄▃▂▁
1.5, 0.5 3.5, 2.5 5.5, 4.5 7.5, 6.5
▂▁▄▃▆▅█▇```

## LiveCode

`command sparklines listOfNums    local utfbase=0x2581    local tStats, utfp, tmin,tmax,trange    put listOfNums into tStats    replace ", " with space in tStats    replace space with comma in tStats    put min(tStats) into tmin    put max(tStats) into tmax    put tmax - tmin into trange    put "Min:" && tmin && tab into plot    put "Max:" && tmax && tab after plot    put "Range:" && trange && tab after plot    put "Mean" && average(tStats) && tab after plot    put "Stdev:" && standardDeviation(tStats) && tab after plot    put "Variance:" && variance(tStats) && return after plot     repeat for each item i in tStats        put  (round(i - tmin/trange * 7)) + utfbase into utfp         put numToCodepoint(utfp) after plot    end repeat    put plotend sparklines`

Test

```sparklines("1 2 3 4 5 6 7 8 7 6 5 4 3 2 1")
Min: 1 	Max: 8 	Range: 7 	Mean 4.266667 	Stdev: 2.250926 	Variance: 5.066667
▁▂▃▄▅▆▇█▇▆▅▄▃▂▁

sparklines("1.5, 0.5 3.5, 2.5 5.5, 4.5 7.5, 6.5")
Min: 0.5 	Max: 7.5 	Range: 7 	Mean 4 	Stdev: 2.44949 	Variance: 6
▂▁▄▃▆▅█▇```

## NetRexx

`/* NetRexx */options replace format comments java crossref symbols nobinary runSample(arg)return -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~method sparkline(spark) private static  spark = spark.changestr(',', ' ')  bars = '\u2581 \u2582 \u2583 \u2584 \u2585 \u2586 \u2587 \u2588'  barK = bars.words()  nmin = spark.word(1)  nmax = nmin  -- get min & max values  loop iw = 1 to spark.words()    nval = spark.word(iw)    nmin = nval.min(nmin)    nmax = nval.max(nmax)    end iw  range = nmax - nmin + 1  slope = ''  loop iw = 1 to spark.words()        point = Math.ceil((spark.word(iw) - nmin + 1) / range * barK)    slope = slope || bars.word(point)    end iw  return slope nmin nmax range -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~method runSample(arg) private static  -- sample data setup  parse arg vals  sparks = 0  sparks[0] = 0  if vals = '' then do    si = sparks[0] + 1; sparks[0] = si; sparks[si] = 1 2 3 4 5 6 7 8 7 6 5 4 3 2 1    si = sparks[0] + 1; sparks[0] = si; sparks[si] = '1.5, 0.5 3.5, 2.5 5.5, 4.5 7.5, 6.5'    end  else do    loop until vals = ''      -- split input on a ! character      parse vals lst '!' vals      si = sparks[0] + 1; sparks[0] = si; sparks[si] = lst      end    end  -- run the samples  loop si = 1 to sparks[0]    vals = sparks[si]    parse sparkline(vals) slope .    say 'Input:        ' vals    say 'Sparkline:    ' slope    say    end si   return `
Output:
```Input:         1 2 3 4 5 6 7 8 7 6 5 4 3 2 1
Sparkline:     ▁▂▃▄▅▆▇█▇▆▅▄▃▂▁

Input:         1.5, 0.5 3.5, 2.5 5.5, 4.5 7.5, 6.5
Sparkline:     ▂▁▄▃▆▅█▇
```

## Mathematica

`toSparkline[data_String] := FromCharacterCode[Round[7 [email protected]@ImportString[data, "Table", "FieldSeparators" -> {" ", ","}]] + 16^^2581];`
```toSparkline["1 2 3 4 5 6 7 8 7 6 5,4 3 2 1 "]
▁▂▃▄▅▆▇█▇▆▅▄▃▂▁

toSparkline[" 1.5, 0.5 3.5, 2.5 5.5, 4.5 7.5, 6.5 "]
▂▁▄▃▆▅█▇
```

## Nim

Translation of: Python
`import rdstdin, strutils, unicode const bar = [9601, 9602, 9603, 9604, 9605, 9606, 9607, 9608]const barcount = float(bar.high) while True:  let    line = readLineFromStdin "Numbers please separated by space/commas: "    numbers = line.split({' ',','}).map(parseFloat)    mn = min(numbers)    mx = max(numbers)    extent = mx - mn  var sparkline = ""  for n in numbers:    let i = int((n-mn) / extent * barcount)    sparkline.add(\$TRune(bar[i]))  echo "min: ", mn.formatFloat(precision = 0), "; max: ", mx.formatFloat(precision = 0)  echo sparkline`
Output:
```Numbers please separated by space/commas: 1 2 3 4 5 6 7 8 7 6 5 4 3 2 1
min: 1; max: 8
▁▂▃▄▅▆▇█▇▆▅▄▃▂▁
Numbers please separated by space/commas: 1.5, 0.5 3.5, 2.5 5.5, 4.5 7.5, 6.5
min: 0.5; max: 7.5
▂▁▄▃▆▅█▇```

## Perl

`binmode(STDOUT, ":utf8"); sub sparkline {  my \$s = shift;  my @n = split(/[\s,]+/,\$s);  return unless @n;  my(\$min,\$max) = (\$n[0],\$n[0]);  for my \$v (@n) { \$min = \$v if \$v < \$min; \$max = \$v if \$v > \$max; }  printf "min: %5f; max %5f\n", \$min, \$max;  my @bars = map { chr(\$_) } 0x2581 .. 0x2588;  my \$div = (\$max - \$min) / \$#bars;  print join("", map { \$bars[\$div ? (\$_-\$min) / \$div : @bars/2] } @n), "\n";  1;} while (1) {  print "Numbers separated by spaces/commas: ";  exit unless sparkline(scalar(<>));}`
Output:
```Numbers separated by spaces/commas: 1 2 3 4 5 6 7 8 7 6 5 4 3 2 1
min: 1.000000; max 8.000000
▁▂▃▄▅▆▇█▇▆▅▄▃▂▁
Numbers separated by spaces/commas: 1.5, 0.5 3.5, 2.5 5.5, 4.5 7.5, 6.5"
min: 0.500000; max 7.500000
▂▁▄▃▆▅█▇
Numbers separated by spaces/commas: 9 18 27 36 45 54 63 72 63 54 45 36 27 18 9
min: 9.000000; max 72.000000
▁▂▃▄▅▆▇█▇▆▅▄▃▂▁
Numbers separated by spaces/commas: 3 2 1 0 -1 -2 -3 -4 -3 -2 -1 0 1 2 3
min: -4.000000; max 3.000000
█▇▆▅▄▃▂▁▂▃▄▅▆▇█
Numbers separated by spaces/commas: 12 12 12 12
min: 12.000000; max 12.000000
▁▁▁▁
Numbers separated by spaces/commas:
```

## Perl 6

`constant @bars = '▁' ... '█';while prompt 'Numbers separated by anything: ' -> \$_ {    my @numbers = map +*, .comb(/ '-'? \d+ ['.' \d+]? /);    my (\$mn,\$mx) = @numbers.minmax.bounds;    say "min: \$mn.fmt('%5f'); max: \$mx.fmt('%5f')";    my \$div = (\$mx - \$mn) / (@bars - 1);    say @bars[ (@numbers X- \$mn) X/ \$div ].join;}`
Output:
```Numbers separated by anything: 9 18 27 36 45 54 63 72 63 54 45 36 27 18 9
9 18 27 36 45 54 63 72 63 54 45 36 27 18 9
min: 9.000000; max: 72.000000
▁▂▃▄▅▆▇█▇▆▅▄▃▂▁
Numbers separated by anything: 1.5, 0.5 3.5, 2.5 5.5, 4.5 7.5, 6.5
1.5 0.5 3.5 2.5 5.5 4.5 7.5 6.5
min: 0.500000; max: 7.500000
▂▁▄▃▆▅█▇
Numbers separated by anything: 3 2 1 0 -1 -2 -3 -4 -3 -2 -1 0 1 2 3
min: -4.000000; max: 3.000000
█▇▆▅▄▃▂▁▂▃▄▅▆▇█
Numbers separated by anything: ^D```

## PicoLisp

`(de sparkLine (Lst)   (let (Min (apply min Lst)  Max (apply max Lst)  Rng (- Max Min))      (for N Lst         (prin            (char (+ 9601 (*/ (- N Min) 7 Rng)) ) ) )      (prinl) ) )`

Test:

`(sparkLine (str "1 2 3 4 5 6 7 8 7 6 5 4 3 2 1"))(sparkLine (scl 1 (str "1.5, 0.5 3.5, 2.5 5.5, 4.5 7.5, 6.5")))`

Output:

```▁▂▃▄▅▆▇█▇▆▅▄▃▂▁
▂▁▄▃▆▅█▇```

## Python

`import retry: raw_inputexcept: raw_input = input # Unicode: 9601, 9602, 9603, 9604, 9605, 9606, 9607, 9608try: bar = u'▁▂▃▄▅▆▇█'except: bar = '▁▂▃▄▅▆▇█'barcount = len(bar) - 1while True:    line = raw_input('Numbers please separated by space/commas: ')    numbers = [float(n) for n in re.split(r'[\s,]+', line.strip())]    mn, mx = min(numbers), max(numbers)    extent = mx - mn    sparkline = ''.join(bar[int( (n - mn) / extent * barcount)]                        for n in numbers)    print('min: %5f; max: %5f' % (mn, mx))    print(sparkline)`
Output:
```Numbers separated by space/commas: 1 2 3 4 5 6 7 8 7 6 5 4 3 2 1
min: 1.000000; max: 7.000000
▁▂▃▄▅▆▇█▇▆▅▄▃▂▁
Numbers separated by space/commas: 1.5, 0.5 3.5, 2.5 5.5, 4.5 7.5, 6.5
min: 0.500000; max: 7.500000
▂▁▄▃▆▅█▇```

## REXX

### version 1

Translation of: NetRexx
Works with: ooRexx
Works with: Regina version 3.4
`/* Rexx */ parse arg aaacall runSample aaareturn -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sparkline:  procedure  parse arg spark  spark = changestr(',', spark, ' ')  bars = '▁ ▂ ▃ ▄ ▅ ▆ ▇ █'  barK = words(bars)  nmin = word(spark, 1)  nmax = nmin  -- get min & max values  do iw = 1 to words(spark)    nval = word(spark, iw)    nmin = min(nval, nmin)    nmax = max(nval, nmax)    end iw  range = nmax - nmin + 1  slope = ''  do iw = 1 to words(spark)    point = ceiling((word(spark, iw) - nmin + 1) / range * barK)    slope = slope || word(bars, point)    end iw  return slope nmin nmax range -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ceiling:procedure   parse arg ceil  return trunc(ceil) + (ceil > 0) * (ceil \= trunc(ceil))floor:procedure   parse arg flor  return trunc(flor) - (flor < 0) * (flor \= trunc(flor)) -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~runSample:procedure  -- sample data setup  parse arg vals  sparks = 0  sparks.0 = 0  if vals = '' then do    si = sparks.0 + 1; sparks.0 = si; sparks.si = 1 2 3 4 5 6 7 8 7 6 5 4 3 2 1    si = sparks.0 + 1; sparks.0 = si; sparks.si = '1.5, 0.5 3.5, 2.5 5.5, 4.5 7.5, 6.5'    end  else do    do until vals = ''      -- split input on a ! character      parse var vals lst '!' vals      si = sparks.0 + 1; sparks.0 = si; sparks.si = lst      end    end  -- run the samples  do si = 1 to sparks.0    vals = sparks.si    parse value sparkline(vals) with slope .    say 'Input:        ' vals    say 'Sparkline:    ' slope    say    end si   return `
Output:
```Input:         1 2 3 4 5 6 7 8 7 6 5 4 3 2 1
Sparkline:     ▁▂▃▄▅▆▇█▇▆▅▄▃▂▁

Input:         1.5, 0.5 3.5, 2.5 5.5, 4.5 7.5, 6.5
Sparkline:     ▂▁▄▃▆▅█▇
```

### version 2

(A re-work of REXX version 1)
This version works on:

• all versions of Regina (which may or may not support single line comments)
• R4 and ROO (which don't support single line comments)
• older versions of REXX such as PC/REXX and Personal REXX which don't support the changestr BIF

This version also removed some dead code, simplified the program structure and subroutines, added comments.
Single line comments were introduced in Regina 3.4.
Regina 3.6 introducted the options:   single_line_comments and noSingle_line_comments.
It should also be noted that the CMS and TSO versions of REXX (and others) don't support single line comments.

`/*REXX program displays a sparkline (spark graph) for a group of values.*/if arg()==0  then do                   /*No arguments?     Use defaults.*/                  call sparkGraph  1 2 3 4 5 6 7 8 7 6 5 4 3 2 1                  call sparkGraph '1.5, 0.5 3.5, 2.5 5.5, 4.5 7.5, 6.5'                  end             else call sparkGraph arg(1)exit                                   /*stick a fork in it, we're done.*//*──────────────────────────────────CEIL subroutine─────────────────────*/ceil: procedure;  parse arg ?;         _=trunc(?);   return _+(?>0)*(?\=_)/*──────────────────────────────────SPARKGRAPH subroutine───────────────*/sparkGraph: procedure;  parse arg x;   say ' input: '  x   /*echo values*/x=translate(x, ' ', ",")               /*remove any superfluous commas. */\$='▁▂▃▄▅▆▇█'                       /*chars to be used for the graph.*/xmin=word(x,1);  xmax=xmin             /*assume a minimum and a maximum.*/     do n=2  to words(x);  _=word(x,n)  /*examine successive words in  X.*/    xmin=min(_,xmin)                   /*find the minimum value in  X.  */    xmax=max(_,xmax)                   /*  "   "  maximum   "    "  "   */    end   /*n*/ z=;      do j=1  for words(x)          /*build the output spark graph.  */         z=z||substr(\$,ceil((word(x,j)-xmin+1)/(xmax-xmin+1)*length(\$)),1)         end   /*j*/ say 'output: '   z;   say              /*show the output, + a blank line*/return`
Output:
using the default input(s):
``` input:  1 2 3 4 5 6 7 8 7 6 5 4 3 2 1
output:  ▁▂▃▄▅▆▇█▇▆▅▄▃▂▁

input:  1.5, 0.5 3.5, 2.5 5.5, 4.5 7.5, 6.5
output:  ▂▁▄▃▆▅█▇
```

## Racket

` #lang racket (require syntax/parse) (define bars "▁▂▃▄▅▆▇█")(define bar-count (string-length bars)) (define (sparks str)  (define ns (map string->number (string-split str #rx"[ ,]" #:repeat? #t)))  (define mn (apply min ns))  (define bar-width (/ (- (apply max ns) mn) (- bar-count 1)))  (apply string (for/list ([n ns]) (string-ref bars (exact-floor (/ (- n mn) bar-width)))))) (sparks "1 2 3 4 5 6 7 8 7 6 5 4 3 2 1")(sparks "1.5, 0.5 3.5, 2.5 5.5, 4.5 7.5, 6.5") `
Output:
` "▁▂▃▄▅▆▇█▇▆▅▄▃▂▁""▂▁▄▃▆▅█▇" `

## Ruby

Translation of: Perl 6
with added protection for input like "0 0 0 0".
`bar = ('▁'..'█').to_a loop {print 'Numbers please separated by space/commas: '  numbers = gets.split(/[\s,]+/).map(&:to_f)  min, max = numbers.minmax  puts "min: %5f; max: %5f"% [min, max]  div = (max - min) / (bar.size - 1)  puts min == max ? bar.last*numbers.size : numbers.map{|num| bar[((num - min) / div).to_i]}.join}`
Output:
Used Go testcases
```Numbers please separated by space/commas: 1 2 3 4 5 6 7 8 7 6 5 4 3 2 1
min: 1.000000; max: 8.000000
▁▂▃▄▅▆▇█▇▆▅▄▃▂▁
Numbers please separated by space/commas: 1.5, 0.5 3.5, 2.5 5.5, 4.5 7.5, 6.5
min: 0.500000; max: 7.500000
▂▁▄▃▆▅█▇
Numbers please separated by space/commas: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
min: 1.000000; max: 24.000000
▁▁▁▁▂▂▂▃▃▃▄▄▄▄▅▅▅▆▆▆▇▇▇█
Numbers please separated by space/commas: 0 99 101 699 701 800
min: 0.000000; max: 800.000000
▁▁▁▇▇█
Numbers please separated by space/commas: 0 -.09 -.11 -.69 -.71 -.8
min: -0.800000; max: 0.000000
█▇▇▁▁▁
Numbers please separated by space/commas: 3 3 3
min: 3.000000; max: 3.000000
███
Numbers please separated by space/commas: 1e99
min: 999999999999999967336168804116691273849533185806555472917961779471295845921727862608739868455469056.000000; max: 999999999999999967336168804116691273849533185806555472917961779471295845921727862608739868455469056.000000
█
```

## Rust

` const BARS: &'static str = "▁▂▃▄▅▆▇█"; fn print_sparkline(s: &str){    let v = BARS.chars().collect::<Vec<char>>();    let line: String = s.replace(",", " ").split(" ")                            .filter(|x| !x.is_empty())                            .map(|x| v[x.parse::<f64>().unwrap().ceil() as usize - 1])                            .collect();    println!("{:?}", line);} fn main(){    let s1 = "1 2 3 4 5 6 7 8 7 6 5 4 3 2 1";    print_sparkline(s1);    let s2 = "1.5, 0.5 3.5, 2.5 5.5, 4.5 7.5, 6.5";    print_sparkline(s2);} `
Output:

"▁▂▃▄▅▆▇█▇▆▅▄▃▂▁"

"▂▁▄▃▆▅█▇"

## S-lang

` % Just to demonstrate alternate ways of defining unicode:private variable spchrs = "\u{2581}\u{2582}\u{2583}\u{2584}\u{2585}\u{2586}\u{2587}\u{2588}";private variable spchrs_alt = "▁▂▃▄▅▆▇█"; define sparkline(arrstr) {    variable a = strtok(arrstr, " \t,"), alen = length(a), out = "";    a = atof(a);    variable amin = min(a), amax = max(a), span = amax - amin, i, d;     _for i (0, alen-1, 1)        if (span != 0) {            % int() truncates; adding .5 here to round:            d = int((a[i] - amin) * 7.0 / span + 0.5);            out += substr(spchrs, d+1, 1);        }        else            out += substr(spchrs, 4, 1);     print(out);} if (not _slang_utf8_ok) error("Sorry, UTF8 mode is not on.");sparkline("1 2 3 4 5 6 7 8 7 6 5 4 3 2 1");sparkline("1.5, 0.5 3.5, 2.5 5.5, 4.5 7.5, 6.5 "); `
Output:

"▁▂▃▄▅▆▇█▇▆▅▄▃▂▁"

"▂▁▄▃▆▅█▇"

## Scala

`def mkSparks( numStr:String ) : String =  numStr.split( "[\\s,]+" ).map(_.toFloat) match {    case v if v.isEmpty => ""    case v if v.length == 1 => "\u2581"    case v =>      (for( i <- v;            s = "\u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588".toCharArray;            d = (v.max - v.min) / (s.length - 1)       ) yield s( ((i - v.min) / d).toInt)).mkString  } println( mkSparks( "1 2 3 4 5 6 7 8 7 6 5 4 3 2 1" ) )println( mkSparks( "1.5, 0.5 3.5, 2.5 5.5, 4.5 7.5, 6.5" ) ) // A random test...println( mkSparks( Stream.continually( math.abs(util.Random.nextInt % 8)).take(64).mkString(" ") ))`
Output:
``` ▁▂▃▄▅▆▇█▇▆▅▄▃▂▁
▂▁▄▃▆▅█▇
▆▁▅▄▁▅▅▇▂▅▆▄▆▃█▄▃▅▇▂▅▆▂▃▇▆▅▇▅█▂▅▄▂▃▇▁▃▇▇▃▁▆▆▂▄▁▄▂▁▁▃▇▆▃▂▆▂▆▇▁▁▆▃```

## Seed7

`\$ include "seed7_05.s7i";  include "scanfile.s7i";  include "float.s7i";  include "utf8.s7i"; const func array float: readDataLine is func  result    var array float: data is 0 times 0.0;  begin    write("Numbers separated by anything: ");    IN.bufferChar := getc(IN);    skipSpace(IN);    while IN.bufferChar <> '\n' do      data &:= float parse getNumber(IN);      skipSpace(IN);      if IN.bufferChar = ',' then        IN.bufferChar := getc(IN);      end if;      skipSpace(IN);    end while;  end func;  const proc: main is func  local    const string: bars is "▁▂▃▄▅▆▇█";    var array float: data is 0 times 0.0;    var float: min is 0.0;    var float: max is 0.0;    var float: number is 0.0;    var integer: index is 0;  begin    OUT := STD_UTF8_OUT;    data  := readDataLine;    while length(data) >= 1 do      min := data[1];      max := data[1];      for number range data do        if number < min then          min := number;        end if;        if number > max then          max := number;        end if;      end for;      for number range data do        index := succ(min(trunc((number - min) * 8.0 / max), 7));        write(bars[index]);      end for;      writeln;      data  := readDataLine;    end while;  end func;`
Output:
```Numbers separated by anything: 1 2 3 4 5 6 7 8 7 6 5 4 3 2 1
▁▂▃▄▅▆▇█▇▆▅▄▃▂▁
Numbers separated by anything: 1.5, 0.5 3.5, 2.5 5.5, 4.5 7.5, 6.5
▂▁▄▃▆▅█▇
Numbers separated by anything:
```

## Sidef

Translation of: Ruby
`var bar = @('▁'..'█');loop {    print 'Numbers, please, separated by space/commas: ';    var numbers = read(String).trim.split(/[\s,]+/).map{.to_n};    var (min, max) = numbers.minmax;    say "min: %5f; max: %5f"%(min, max);    var div = ((max - min) / bar.end);    say (min == max ? bar.last*numbers.len : numbers.map{|num| bar[(num - min) / div]}.join);}`
Output:
```Numbers, please, separated by space/commas: 1 2 3 4 5 6 7 8 7 6 5 4 3 2 1
min: 1.000000; max: 8.000000
▁▂▃▄▅▆▇█▇▆▅▄▃▂▁
Numbers, please, separated by space/commas: 1.5, 0.5 3.5, 2.5 5.5, 4.5 7.5, 6.5
min: 0.500000; max: 7.500000
▂▁▄▃▆▅█▇```

## Tcl

Works with: Tcl version 8.6
`package require Tcl 8.6 proc extractValues {series} {    return [regexp -all -inline {\d+(?:\.\d*)?|\.\d+} \$series]}proc renderValue {min max value} {    set band [expr {int(8*(\$value-\$min)/((\$max-\$min)*1.01))}]    return [format "%c" [expr {0x2581 + \$band}]]}proc sparkline {series} {    set values [extractValues \$series]    set min [tcl::mathfunc::min {*}\$values]    set max [tcl::mathfunc::max {*}\$values]    return [join [lmap v \$values {renderValue \$min \$max \$v}] ""]}`

Demonstrating:

`set data {    "1 2 3 4 5 6 7 8 7 6 5 4 3 2 1"    "1.5, 0.5 3.5, 2.5 5.5, 4.5 7.5, 6.5"}foreach series \$data {    puts "Series: \$series"    puts "Sparkline: [sparkline \$series]"}`
Output:
```Series: 1 2 3 4 5 6 7 8 7 6 5 4 3 2 1
Sparkline: ▁▂▃▄▅▆▇█▇▆▅▄▃▂▁
Series: 1.5, 0.5 3.5, 2.5 5.5, 4.5 7.5, 6.5
Sparkline: ▂▁▄▃▆▅█▇
```

## zkl

`var sparks=[0x2581..0x2588].apply("toString",-8); // int.toString(-8)-->UTF-8var sl=(sparks.len()-1); fcn sparkLine(xs){   min:=(0.0).min(xs); max:=(0.0).max(xs);  // min/max are float reguardless of xs   range:=max-min;  // float   println("Range [",min,"-",max,"]", xs);   xs.pump(String,'wrap(x){ sparks[(x - min)*sl/range] }).println();}`
`one:="1 2 3 4 5 6 7 8 7 6 5 4 3 2 1".split(" ").apply("toInt");two:=("1.5, 0.5 3.5, 2.5 5.5 4.5 7.5, 6.5" - ",").split(" ").apply("toFloat");sparkLine(one); sparkLine(two);`
Output:
```Range [1-8]: L(1,2,3,4,5,6,7,8,7,6,5,4,3,2,1)
▁▂▃▄▅▆▇█▇▆▅▄▃▂▁
Range [0.5-7.5]: L(1.5,0.5,3.5,2.5,5.5,4.5,7.5,6.5)
▂▁▄▃▆▅█▇
```