make sure no duplicate records exists during eval and merge (#5633)

This commit is contained in:
WindSoilder 2022-05-26 08:10:31 +08:00 committed by GitHub
parent 8e98df8b28
commit 9602e82029
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 77 additions and 10 deletions

View File

@ -59,6 +59,14 @@ impl Command for Merge {
vec![Value::test_int(1), Value::test_int(2), Value::test_int(3)],
)),
},
Example {
example: "{a: 1, b: 3} | merge { {b: 2, c: 4} }",
description: "Merge two records with overlap key",
result: Some(Value::test_record(
vec!["a", "b", "c"],
vec![Value::test_int(1), Value::test_int(2), Value::test_int(4)],
)),
},
]
}
@ -106,8 +114,10 @@ impl Command for Merge {
(Ok((inp_cols, inp_vals)), Some(to_merge)) => {
match to_merge.as_record() {
Ok((to_merge_cols, to_merge_vals)) => {
let cols = [inp_cols, to_merge_cols].concat();
let vals = [inp_vals, to_merge_vals].concat();
let (cols, vals) = do_merge(
(inp_cols.to_vec(), inp_vals.to_vec()),
(to_merge_cols.to_vec(), to_merge_vals.to_vec()),
);
Value::Record {
cols,
vals,
@ -146,12 +156,10 @@ impl Command for Merge {
..,
),
) => {
let mut cols = inp_cols.to_vec();
cols.extend(to_merge_cols.to_vec());
let mut vals = inp_vals.to_vec();
vals.extend(to_merge_vals.to_vec());
let (cols, vals) = do_merge(
(inp_cols.to_vec(), inp_vals.to_vec()),
(to_merge_cols.to_vec(), to_merge_vals.to_vec()),
);
Ok(Value::Record {
cols,
vals,
@ -181,6 +189,29 @@ impl Command for Merge {
}
}
fn do_merge(
input_record: (Vec<String>, Vec<Value>),
to_merge_record: (Vec<String>, Vec<Value>),
) -> (Vec<String>, Vec<Value>) {
let (mut result_cols, mut result_vals) = input_record;
let (to_merge_cols, to_merge_vals) = to_merge_record;
for (col, val) in to_merge_cols.into_iter().zip(to_merge_vals) {
let pos = result_cols.iter().position(|c| c == &col);
// if find, replace existing data, else, push new data.
match pos {
Some(index) => {
result_vals[index] = val;
}
None => {
result_cols.push(col);
result_vals.push(val);
}
}
}
(result_cols, result_vals)
}
#[cfg(test)]
mod test {
use super::*;

View File

@ -81,3 +81,29 @@ fn ignores_duplicate_columns_rejected() {
assert_eq!(actual.out, "last name");
}
#[test]
fn reject_record_from_raw_eval() {
let actual = nu!(
cwd: ".", pipeline(
r#"
{"a": 3, "a": 4} | reject a | describe
"#
)
);
assert!(actual.out.contains("record<>"));
}
#[test]
fn reject_table_from_raw_eval() {
let actual = nu!(
cwd: ".", pipeline(
r#"
[{"a": 3, "a": 4}] | reject a
"#
)
);
assert!(actual.out.contains("record 0 fields"));
}

View File

@ -472,8 +472,18 @@ pub fn eval_expression(
let mut cols = vec![];
let mut vals = vec![];
for (col, val) in fields {
cols.push(eval_expression(engine_state, stack, col)?.as_string()?);
vals.push(eval_expression(engine_state, stack, val)?);
// avoid duplicate cols.
let col_name = eval_expression(engine_state, stack, col)?.as_string()?;
let pos = cols.iter().position(|c| c == &col_name);
match pos {
Some(index) => {
vals[index] = eval_expression(engine_state, stack, val)?;
}
None => {
cols.push(col_name);
vals.push(eval_expression(engine_state, stack, val)?);
}
}
}
Ok(Value::Record {