javascript 向每个类别仅添加一次新记录

hgqdbh6s  于 2022-12-10  发布在  Java
关注(0)|答案(5)|浏览(120)

我有一个可以重复catId的目录数组。
每个目录都有唯一的segmentId

const catalogs = [
  { catID: 2, segmentId: '2', segmentName: 'S2' },
  { catID: 1, segmentId: '1', segmentName: 'S1' },
  { catID: 1, segmentId: '2', segmentName: 'S2' }
];

意味着每个catId可能有多个segmentId,但该特定catId的segmentId不能重复
现在,我要将区段数组指定给主目录数组:
例如,下面是我需要分配的段数组示例:

[{id: '3', name: 'S3'}]

我期待您的回复:

[
  { catID: 2, segmentId: '2', segmentName: 'S2' },
  { catID: 2, segmentId: '3', segmentName: 'S3' },
  { catID: 1, segmentId: '1', segmentName: 'S1' },
  { catID: 1, segmentId: '2', segmentName: 'S2' },
  { catID: 1, segmentId: '3', segmentName: 'S3' },
]

我自己也尝试过解决这个问题,我非常接近,但没有得到预期的结果:

const catalogs = [
  { catID: 2, segmentId: '2', segmentName: 'S2' },
  { catID: 1, segmentId: '1', segmentName: 'S1' },
  { catID: 1, segmentId: '2', segmentName: 'S2' }
];

function addSegmentsToCatalogs(segments){

    let catalogUid = catalogs[0].catID;
    const lastCatalog = catalogs.slice(-1).pop();
    const insertionArray = [];
    
  catalogs.forEach(async (catalog) => {
    if (catalogUid != catalog.catID) {
      segments.forEach((segment) => {
        insertionArray.push({
          catalogId: catalogUid,
          segmentId: segment.id,
          segmentName: segment.name,
        });
      });

      catalogUid = catalog.catID;
    }

    if (!segments.some((segment) => segment.id === catalog.segmentId)) {

      segments.push({ id: catalog.segmentId, name: catalog.segmentName });

      if (
        catalog.catID == lastCatalog.catID &&
        catalog.segmentId == lastCatalog.segmentId
      ) {
        segments.forEach((segment) => {
          insertionArray.push({
            catalogId: catalogUid,
            segmentId: segment.id,
            segmentName: segment.name,
          });
        });
      }
    }
    });
  
  console.log(insertionArray);
  return insertionArray;
}

addSegmentsToCatalogs([{id: '3', name: 'S3'}])

有谁能帮我找到问题所在或建议我更好的解决方法吗?

oymdgrw7

oymdgrw71#

您可以采用嵌套的Map,并按catIDsegmentId分组。
map看起来像这样:

2:
    '2': { catID: 2, segmentId: '2', segmentName: 'S2' }
1:
    '1': { catID: 1, segmentId: '1', segmentName: 'S1' }
    '2': { catID: 1, segmentId: '2', segmentName: 'S2' }

在拥有了一个嵌套Map的Map之后,你可以获取外部Map的键,并将新的段添加到内部Map的新键中。
最后取一个所有值的平面数组。
第一次

wrrgggsh

wrrgggsh2#

下面提供的解决方案实现了一种使用预处理的查找数据的方法...

  • catIDsset(用于现有目录ID)
  • 和段ID的现有组合的catSegments对象。

中间结果是新创建的有效目录项的数组,然后通过经由flatMap在现有catID的数组上迭代来计算,其中对于每个catID,通过对传递的segments数组执行reduce来尝试创建新目录项的数组。每个可能的目录项的有效性是通过查找它是否已作为相同catIDsegmentId值的组合存在来确保的。
第一个

rnmwe5a2

rnmwe5a23#

我们可以通过添加一个groupBy实用函数来实现这一点,这在许多库中都可以找到。
第一个
我们首先使用groupBy将输入分成基于共享catID的组,得到如下结果:

{
    "1": [
        {catID: 1, segmentId: "1", segmentName: "S1"},
        {catID: 1, segmentId: "2", segmentName: "S2"}
    ],
    "2": [
        {catID: 2, segmentId: "2", segmentName: "S2"}
    ]
}

在上面调用Object .entries,给予如下结果:

[
  ["1", [{"catID": 1, "segmentId": "1", "segmentName": "S1"}, {"catID": 1, "segmentId": "2", "segmentName": "S2"}]], 
  ["2", [{"catID": 2, "segmentId": "2", "segmentName": "S2"}]]
]

现在,我们通过调用flatMap,向其传递一个Map新段的函数,创建与主记录匹配的对象,唯一有点奇怪的是我们必须通过将catID Package 在Number中来转换catID。我们需要这样做是因为当我们在groupBy中创建一个带有数字键的对象时,它们会在后台被转换成字符串。我们可以使用Map来代替,从而避免这个问题,但会添加稍微复杂的语法。
我们可以轻松地将groupBy函数内联到主函数中,但这是一个可广泛重用函数;如果您还没有使用Ramda、lodash或Underscore之类的工具,我建议您在实用程序库中保留一些这样的工具。

hkmswyz6

hkmswyz64#

const catalogs = [{
    catID: 2,
    segmentId: '2',
    segmentName: 'S2'
  },
  {
    catID: 1,
    segmentId: '1',
    segmentName: 'S1'
  },
  {
    catID: 1,
    segmentId: '2',
    segmentName: 'S2'
  }
];

function addSegmentsToCatalogs(segments) {

    const copyOfCatlogs = [...catalogs];

  const catlogIds = catalogs.map(c => c.catID);
  const uniqCatIds = [...new Set(catlogIds)];
  const segIds = segments.map(s => s.id);
  

  uniqCatIds.forEach(catId => {

    segIds.forEach(segId => {
      const hasCatIdWithSegment = copyOfCatlogs.findIndex(c => c.catID == catId && segId == c.segmentId);
      if (hasCatIdWithSegment == -1) {
        const newSegment = segments.find(s => s.id == segId);
        copyOfCatlogs.push({
          catID: catId,
          segmentId: newSegment.id,
          segmentName: newSegment.name,
        });
      }
    })
  })
  
  console.log(copyOfCatlogs);

  return copyOfCatlogs;
}

addSegmentsToCatalogs([{
  id: '3',
  name: 'S3'
}])
8yparm6h

8yparm6h5#

在得到答案之前:输入数据表明输入段名称字符串的模式是大写字母S后跟一个整数序列。number类型可以更好地表示数据集输出数组中每个对象的segmentId值... number可以使用内置方法进行字符串化,成功率为100%-这意味着如果以后需要字符串形式的值,可以根据需要轻松地执行此操作,但反之则不成立:大多数字符串不能被解析为数字。
在上面的上下文中,您可以使用转换函数将每个输入项解析为所需的输出结构(并在此过程中验证值)。请参见下面的示例代码:
TSPlayground

class AssertionError extends Error {
  override name = 'AssertionError';
}

function assert (expr: unknown, msg?: string): asserts expr {
  if (!expr) throw new AssertionError(msg);
}

type OutputItem = {
  catId: number;
  segmentId: number;
  segmentName: string;
};

function transform (item: InputItem): OutputItem {
  const segmentName = item.name;

  const catId = Number(item.id);
  assert(Number.isInteger(catId), `Couldn't parse category ID as integer`);

  const segmentId = Number(segmentName.slice(1));
  assert(Number.isInteger(segmentId), `Couldn't parse segment ID as integer`);

  return {
    catId,
    segmentId,
    segmentName,
  };
}

type InputSegmentName = `S${number}`;
type InputId = `${number}`;
type InputItem = { id: InputId; name: InputSegmentName; };

const input = [
  {id: '2', name: 'S2'},
  {id: '2', name: 'S3'},
  {id: '1', name: 'S1'},
  {id: '1', name: 'S2'},
  {id: '1', name: 'S3'},
  {id: '3', name: 'S3'},
] satisfies InputItem[];

const output = input.map(transform);
console.log(output); /* Logs:
[
  {
    "catId": 2,
    "segmentId": 2,
    "segmentName": "S2"
  },
  {
    "catId": 2,
    "segmentId": 3,
    "segmentName": "S3"
  },
  {
    "catId": 1,
    "segmentId": 1,
    "segmentName": "S1"
  },
  {
    "catId": 1,
    "segmentId": 2,
    "segmentName": "S2"
  },
  {
    "catId": 1,
    "segmentId": 3,
    "segmentName": "S3"
  },
  {
    "catId": 3,
    "segmentId": 3,
    "segmentName": "S3"
  }
]
*/

从TS Playground编译的JS:

"use strict";
class AssertionError extends Error {
    constructor() {
        super(...arguments);
        this.name = 'AssertionError';
    }
}
function assert(expr, msg) {
    if (!expr)
        throw new AssertionError(msg);
}
function transform(item) {
    const segmentName = item.name;
    const catId = Number(item.id);
    assert(Number.isInteger(catId), `Couldn't parse category ID as integer`);
    const segmentId = Number(segmentName.slice(1));
    assert(Number.isInteger(segmentId), `Couldn't parse segment ID as integer`);
    return {
        catId,
        segmentId,
        segmentName,
    };
}
const input = [
    { id: '2', name: 'S2' },
    { id: '2', name: 'S3' },
    { id: '1', name: 'S1' },
    { id: '1', name: 'S2' },
    { id: '1', name: 'S3' },
    { id: '3', name: 'S3' },
];
const output = input.map(transform);
console.log(output); /* Logs:
[
  {
    "catId": 2,
    "segmentId": 2,
    "segmentName": "S2"
  },
  {
    "catId": 2,
    "segmentId": 3,
    "segmentName": "S3"
  },
  {
    "catId": 1,
    "segmentId": 1,
    "segmentName": "S1"
  },
  {
    "catId": 1,
    "segmentId": 2,
    "segmentName": "S2"
  },
  {
    "catId": 1,
    "segmentId": 3,
    "segmentName": "S3"
  },
  {
    "catId": 3,
    "segmentId": 3,
    "segmentName": "S3"
  }
]
*/

更新以回应您的意见:如果您的实际数据集不同(例如,细分名称的格式并不完全与您在问题中所示的格式相同),从而导致上述观察结果不适用,那么当然,您不必将细分标识符解析为数字:
TS Playground中的完整代码

type OutputItem = {
  catId: number;
  segmentId: string;
  segmentName: string;
};

function transform (item: InputItem): OutputItem {
  const segmentName = item.name;

  const catId = Number(item.id);
  assert(Number.isInteger(catId), `Couldn't parse category ID as integer`);

  const segmentId = segmentName.slice(1);

  return {
    catId,
    segmentId,
    segmentName,
  };
}

从TS Playground编译的JS:

"use strict";
class AssertionError extends Error {
    constructor() {
        super(...arguments);
        this.name = 'AssertionError';
    }
}
function assert(expr, msg) {
    if (!expr)
        throw new AssertionError(msg);
}
function transform(item) {
    const segmentName = item.name;
    const catId = Number(item.id);
    assert(Number.isInteger(catId), `Couldn't parse category ID as integer`);
    const segmentId = segmentName.slice(1);
    return {
        catId,
        segmentId,
        segmentName,
    };
}
const input = [
    { id: '2', name: 'S2' },
    { id: '2', name: 'S3' },
    { id: '1', name: 'S1' },
    { id: '1', name: 'S2' },
    { id: '1', name: 'S3' },
    { id: '3', name: 'S3' },
];
const output = input.map(transform);
console.log(output); /* Logs:
[
  {
    "catId": 2,
    "segmentId": "2",
    "segmentName": "S2"
  },
  {
    "catId": 2,
    "segmentId": "3",
    "segmentName": "S3"
  },
  {
    "catId": 1,
    "segmentId": "1",
    "segmentName": "S1"
  },
  {
    "catId": 1,
    "segmentId": "2",
    "segmentName": "S2"
  },
  {
    "catId": 1,
    "segmentId": "3",
    "segmentName": "S3"
  },
  {
    "catId": 3,
    "segmentId": "3",
    "segmentName": "S3"
  }
]
*/

相关问题