将动态HTML字段数组中的值减少为最紧凑的形式

时间:2019-04-25 11:55:37

标签: javascript forms lodash

我有一个带有动态recipients字段数组的HTML表单,其中每个数组项都有nameemail字段。字段名称是使用[array_name].[item_index].[field_name]模式唯一生成的。当我提交表单时,它将生成以下JSON:

[{
  name: 'recipients.0.name',
  value: 'John'
}, {
  name: recipients.0.email',
  value: 'john@test.com'
},{
  name: 'recipients.1.name',
  value: 'Sam'
}, {
  name: recipients.1.email',
  value: 'sam@test.com'
}]

我需要将其转换为最紧凑的形式,如下所示:

{
  recipients: [{
    name: 'John',
    email: 'john@test.com'
  }, {
    name: 'Sam',
    email: 'sam@test.com'
  }]
}

最优雅的方法是什么?欢迎使用Lodash的示例。

编辑:仅提供字段名称。解决方案应适用于模式[array_name].[item_index].[field_name]之后的任何名称,而不仅限于recipients.[item_index].[field_name]

这是我的实现,对此我不太满意:

const fields = [{
    name: 'recipients.0.name',
    value: 'John',
  },
  {
    name: 'recipients.0.email',
    value: 'john@test.com',
  },
  {
    name: 'recipients.1.name',
    value: 'Sam',
  },
  {
    name: 'recipients.1.email',
    value: 'sam@test.com',
  },
];

const compactFieldArrays = fields.reduce((result, field) => {
  const [fieldArrayName, fieldArrayItemIndex, fieldName] = field.name.split('.');
  let fieldArray = result[fieldArrayName];

  if (!fieldArray) {
    fieldArray = [];
    result[fieldArrayName] = fieldArray;
  }

  let fieldArrayItem = fieldArray[fieldArrayItemIndex];
  if (fieldArrayItem) {
    fieldArrayItem[fieldName] = field.value;
  } else {
    fieldArrayItem = {
      [fieldName]: field.value,
    };
    fieldArray.push(fieldArrayItem);
  }
  return result;
}, {});

console.log(compactFieldArrays);

3 个答案:

答案 0 :(得分:2)

仅使用JS:

const data = [{"name":"recipients.0.name","value":"John"},{"name":"recipients.0.email","value":"john@test.com"},{"name":"recipients.1.name","value":"Sam"},{"name":"recipients.1.email","value":"sam@test.com"}]

// `reduce` over the array
const out = data.reduce((acc, { name, value }) => {

  // `split` the name on the period, and pop off the key
  // and the recipient id
  const arr = name.split('.');
  const key = arr.pop();
  const recipient = arr.pop();

  // Create either a new object or update the existing
  // object on the accumulator
  acc[recipient] = { ...acc[recipient] || {}, [key]: value };
  return acc;
}, {});

console.log(Object.values(out));

答案 1 :(得分:1)

对于您的特定用例,我认为我的是最简单的:

具有未知键的字段数目未知

const arr = [{ name: 'recipients.0.name', value: 'John'}, { name: 'recipients.0.email', value: 'john@test.com'},{ name: 'recipients.1.name', value: 'Sam'}, { name: 'recipients.1.email', value: 'sam@test.com'}, { name: 'recipients.1.phone', value: '12345'}]
let recipients = [];
arr.forEach(x => {
  let arr = x.name.split(".");
  let key = arr[2];
  let num = arr[1];
  recipients[num] = recipients[num] || {}
  recipients[num][key]=x.value;
})
console.log(recipients)

已知字段数

const arr = [{ name: 'recipients.0.name', value: 'John'}, { name: 'recipients.0.email', value: 'john@test.com'},{ name: 'recipients.1.name', value: 'Sam'}, { name: 'recipients.1.email', value: 'sam@test.com'}]
let recipients = [];
arr.forEach((x, i) => {
  if (i == 0 || i % 2 == 0) recipients.push({"name": x.value});
  else recipients[recipients.length - 1]["email"]  = x.value;
})
console.log(recipients)

答案 2 :(得分:0)

recipient.{number}.对项目进行分组(使用String.replace()删除字段名称)。然后映射组,并将所有字段对转换为对象,然后将所有组对象合并在一起。

const { flow, groupBy, map, merge } = _

const fn = flow(
  arr => groupBy(arr, ({ name }) => name.replace(/[^.]+$/, '')),
  groups => map(groups, (g, k) => merge(...
    map(g, ({ name, value }) => ({ [name.replace(k, '')]: value }))
  ))
)

const data = [{"name":"recipients.0.name","value":"John"},{"name":"recipients.0.email","value":"john@test.com"},{"name":"recipients.1.name","value":"Sam"},{"name":"recipients.1.email","value":"sam@test.com"}]

const result = fn(data)

console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>

和实现相同想法的lodash/fp版本,但使用_.fromPairs()将每个组转换为对象:

const { flow, groupBy, prop, replace, map, over, fromPairs  } = _

const fn = flow(
  groupBy(flow(prop('name'), replace(/[^.]+$/, ''))),
  map(flow(
    map(flow(
      over([
        flow(prop('name'), replace(/.*\.([^.]+$)/, '$1')),
        prop('value')
      ]),
    )),
    fromPairs
  ))
)

const data = [{"name":"recipients.0.name","value":"John"},{"name":"recipients.0.email","value":"john@test.com"},{"name":"recipients.1.name","value":"Sam"},{"name":"recipients.1.email","value":"sam@test.com"}]

const result = fn(data)

console.log(result)
<script src='https://cdn.jsdelivr.net/g/lodash@4(lodash.min.js+lodash.fp.min.js)'></script>