mirror of
https://github.com/nushell/nushell
synced 2024-11-10 07:04:13 +00:00
Add --number flag to split column (#13831)
This allows parsing of data (e.g. key-value pairs) where the last column may contain the delimiter. - this PR should close #13742 # Description Adds a `--number (-n)` flag to `split column`, analogous to `split row --number`. ``` ~> ['author: Salina Yoon' r#'title: Where's Ellie?: A Hide-and-Seek Book'#] | split column --number 2 ': ' key value ╭───┬────────┬──────────────────────────────────────╮ │ # │ key │ value │ ├───┼────────┼──────────────────────────────────────┤ │ 0 │ author │ Salina Yoon │ │ 1 │ title │ Where's Ellie?: A Hide-and-Seek Book │ ╰───┴────────┴──────────────────────────────────────╯ ``` # User-Facing Changes * `split column` gains a `--number` option # Tests + Formatting Tests included in strings::split::column::test::test_examples and commands::split_column::to_column. # After Submitting Reference documentation is auto-generated from code. No other documentation updates necessary.
This commit is contained in:
parent
fb34a4fc6c
commit
5101b5e306
2 changed files with 59 additions and 5 deletions
|
@ -26,6 +26,12 @@ impl Command for SubCommand {
|
|||
"The character or string that denotes what separates columns.",
|
||||
)
|
||||
.switch("collapse-empty", "remove empty columns", Some('c'))
|
||||
.named(
|
||||
"number",
|
||||
SyntaxShape::Int,
|
||||
"Split into maximum number of items",
|
||||
Some('n'),
|
||||
)
|
||||
.switch("regex", "separator is a regular expression", Some('r'))
|
||||
.rest(
|
||||
"rest",
|
||||
|
@ -91,6 +97,20 @@ impl Command for SubCommand {
|
|||
}),
|
||||
])),
|
||||
},
|
||||
Example {
|
||||
description: "Split into columns, last column may contain the delimiter",
|
||||
example: r"['author: Salina Yoon' r#'title: Where's Ellie?: A Hide-and-Seek Book'#] | split column --number 2 ': ' key value",
|
||||
result: Some(Value::test_list(vec![
|
||||
Value::test_record(record! {
|
||||
"key" => Value::test_string("author"),
|
||||
"value" => Value::test_string("Salina Yoon"),
|
||||
}),
|
||||
Value::test_record(record! {
|
||||
"key" => Value::test_string("title"),
|
||||
"value" => Value::test_string("Where's Ellie?: A Hide-and-Seek Book"),
|
||||
}),
|
||||
])),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
|
@ -108,12 +128,14 @@ impl Command for SubCommand {
|
|||
let separator: Spanned<String> = call.req(engine_state, stack, 0)?;
|
||||
let rest: Vec<Spanned<String>> = call.rest(engine_state, stack, 1)?;
|
||||
let collapse_empty = call.has_flag(engine_state, stack, "collapse-empty")?;
|
||||
let max_split: Option<usize> = call.get_flag(engine_state, stack, "number")?;
|
||||
let has_regex = call.has_flag(engine_state, stack, "regex")?;
|
||||
|
||||
let args = Arguments {
|
||||
separator,
|
||||
rest,
|
||||
collapse_empty,
|
||||
max_split,
|
||||
has_regex,
|
||||
};
|
||||
split_column(engine_state, call, input, args)
|
||||
|
@ -128,12 +150,14 @@ impl Command for SubCommand {
|
|||
let separator: Spanned<String> = call.req_const(working_set, 0)?;
|
||||
let rest: Vec<Spanned<String>> = call.rest_const(working_set, 1)?;
|
||||
let collapse_empty = call.has_flag_const(working_set, "collapse-empty")?;
|
||||
let max_split: Option<usize> = call.get_flag_const(working_set, "number")?;
|
||||
let has_regex = call.has_flag_const(working_set, "regex")?;
|
||||
|
||||
let args = Arguments {
|
||||
separator,
|
||||
rest,
|
||||
collapse_empty,
|
||||
max_split,
|
||||
has_regex,
|
||||
};
|
||||
split_column(working_set.permanent(), call, input, args)
|
||||
|
@ -144,6 +168,7 @@ struct Arguments {
|
|||
separator: Spanned<String>,
|
||||
rest: Vec<Spanned<String>>,
|
||||
collapse_empty: bool,
|
||||
max_split: Option<usize>,
|
||||
has_regex: bool,
|
||||
}
|
||||
|
||||
|
@ -169,7 +194,16 @@ fn split_column(
|
|||
})?;
|
||||
|
||||
input.flat_map(
|
||||
move |x| split_column_helper(&x, ®ex, &args.rest, args.collapse_empty, name_span),
|
||||
move |x| {
|
||||
split_column_helper(
|
||||
&x,
|
||||
®ex,
|
||||
&args.rest,
|
||||
args.collapse_empty,
|
||||
args.max_split,
|
||||
name_span,
|
||||
)
|
||||
},
|
||||
engine_state.signals(),
|
||||
)
|
||||
}
|
||||
|
@ -179,13 +213,20 @@ fn split_column_helper(
|
|||
separator: &Regex,
|
||||
rest: &[Spanned<String>],
|
||||
collapse_empty: bool,
|
||||
max_split: Option<usize>,
|
||||
head: Span,
|
||||
) -> Vec<Value> {
|
||||
if let Ok(s) = v.coerce_str() {
|
||||
let split_result: Vec<_> = separator
|
||||
.split(&s)
|
||||
.filter(|x| !(collapse_empty && x.is_empty()))
|
||||
.collect();
|
||||
let split_result: Vec<_> = match max_split {
|
||||
Some(max_split) => separator
|
||||
.splitn(&s, max_split)
|
||||
.filter(|x| !(collapse_empty && x.is_empty()))
|
||||
.collect(),
|
||||
None => separator
|
||||
.split(&s)
|
||||
.filter(|x| !(collapse_empty && x.is_empty()))
|
||||
.collect(),
|
||||
};
|
||||
let positional: Vec<_> = rest.iter().map(|f| f.item.clone()).collect();
|
||||
|
||||
// If they didn't provide column names, make up our own
|
||||
|
|
|
@ -33,6 +33,19 @@ fn to_column() {
|
|||
|
||||
assert!(actual.out.contains("shipper"));
|
||||
|
||||
let actual = nu!(
|
||||
cwd: dirs.test(), pipeline(
|
||||
r#"
|
||||
open sample.txt
|
||||
| lines
|
||||
| str trim
|
||||
| split column -n 3 ","
|
||||
| get column3
|
||||
"#
|
||||
));
|
||||
|
||||
assert!(actual.out.contains("tariff_item,name,origin"));
|
||||
|
||||
let actual = nu!(
|
||||
cwd: dirs.test(), pipeline(
|
||||
r"
|
||||
|
|
Loading…
Reference in a new issue