make seq more nu-like by returning numbers when possible (#3409)

This commit is contained in:
Darren Schroeder 2021-05-11 12:02:16 -05:00 committed by GitHub
parent b93b80ccaa
commit e73491441a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -1,7 +1,7 @@
use crate::prelude::*; use crate::prelude::*;
use nu_engine::WholeStreamCommand; use nu_engine::WholeStreamCommand;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::value::StrExt; use nu_protocol::value::{DecimalExt, I64Ext, StrExt};
use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value}; use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
use nu_source::Tagged; use nu_source::Tagged;
use std::cmp; use std::cmp;
@ -57,16 +57,33 @@ impl WholeStreamCommand for Seq {
description: "sequence 1 to 10 with newline separator", description: "sequence 1 to 10 with newline separator",
example: "seq 1 10", example: "seq 1 10",
result: Some(vec![ result: Some(vec![
UntaggedValue::string("1").into(), UntaggedValue::int(1).into(),
UntaggedValue::string("2").into(), UntaggedValue::int(2).into(),
UntaggedValue::string("3").into(), UntaggedValue::int(3).into(),
UntaggedValue::string("4").into(), UntaggedValue::int(4).into(),
UntaggedValue::string("5").into(), UntaggedValue::int(5).into(),
UntaggedValue::string("6").into(), UntaggedValue::int(6).into(),
UntaggedValue::string("7").into(), UntaggedValue::int(7).into(),
UntaggedValue::string("8").into(), UntaggedValue::int(8).into(),
UntaggedValue::string("9").into(), UntaggedValue::int(9).into(),
UntaggedValue::string("10").into(), UntaggedValue::int(10).into(),
]),
},
Example {
description: "sequence 1.0 to 2.0 by 0.1s with newline separator",
example: "seq 1.0 0.1 2.0",
result: Some(vec![
UntaggedValue::decimal_from_float(1.0000, Span::default()).into(),
UntaggedValue::decimal_from_float(1.1000, Span::default()).into(),
UntaggedValue::decimal_from_float(1.2000, Span::default()).into(),
UntaggedValue::decimal_from_float(1.3000, Span::default()).into(),
UntaggedValue::decimal_from_float(1.4000, Span::default()).into(),
UntaggedValue::decimal_from_float(1.5000, Span::default()).into(),
UntaggedValue::decimal_from_float(1.6000, Span::default()).into(),
UntaggedValue::decimal_from_float(1.7000, Span::default()).into(),
UntaggedValue::decimal_from_float(1.8000, Span::default()).into(),
UntaggedValue::decimal_from_float(1.9000, Span::default()).into(),
UntaggedValue::decimal_from_float(2.0000, Span::default()).into(),
]), ]),
}, },
Example { Example {
@ -291,8 +308,19 @@ fn print_seq(
) -> OutputStream { ) -> OutputStream {
let mut i = 0isize; let mut i = 0isize;
let mut value = first + i as f64 * step; let mut value = first + i as f64 * step;
// for string output
let mut ret_str = "".to_owned(); let mut ret_str = "".to_owned();
// for number output
let mut ret_num = vec![];
// If the separator and terminator are line endings we can convert to numbers
let use_num =
(separator == "\n" || separator == "\r") && (terminator == "\n" || terminator == "\r");
while !done_printing(value, step, last) { while !done_printing(value, step, last) {
if use_num {
ret_num.push(value);
} else {
// formatting for string output with potential padding
let istr = format!("{:.*}", largest_dec, value); let istr = format!("{:.*}", largest_dec, value);
let ilen = istr.len(); let ilen = istr.len();
let before_dec = istr.find('.').unwrap_or(ilen); let before_dec = istr.find('.').unwrap_or(ilen);
@ -302,6 +330,7 @@ fn print_seq(
} }
} }
ret_str.push_str(&istr); ret_str.push_str(&istr);
}
i += 1; i += 1;
value = first + i as f64 * step; value = first + i as f64 * step;
if !done_printing(value, step, last) { if !done_printing(value, step, last) {
@ -309,13 +338,44 @@ fn print_seq(
} }
} }
if (first >= last && step < 0f64) || (first <= last && step > 0f64) { if !use_num && ((first >= last && step < 0f64) || (first <= last && step > 0f64)) {
ret_str.push_str(&terminator); ret_str.push_str(&terminator);
} }
if use_num {
// we'd like to keep the datatype the same for the output, so check
// and see if any of the output is really decimals, and if it is
// we'll make the entire output decimals
let contains_decimals = vec_contains_decimals(&ret_num);
let rows: Vec<Value> = ret_num
.iter()
.map(|v| {
if contains_decimals {
v.to_value_create_tag()
} else {
let vi64 = *v as i64;
vi64.to_value_create_tag()
}
})
.collect();
(rows.into_iter()).to_output_stream()
} else {
let rows: Vec<Value> = ret_str let rows: Vec<Value> = ret_str
.lines() .lines()
.map(|v| v.to_str_value_create_tag()) .map(|v| v.to_str_value_create_tag())
.collect(); .collect();
(rows.into_iter()).to_output_stream() (rows.into_iter()).to_output_stream()
} }
}
fn vec_contains_decimals(array: &[f64]) -> bool {
let mut found_decimal = false;
for x in array {
if x.fract() != 0.0 {
found_decimal = true;
break;
}
}
found_decimal
}