【问题标题】:JavaScript: SQL-like join of two arraysJavaScript:两个数组的类似 SQL 的连接
【发布时间】:2020-10-16 18:42:19
【问题描述】:

我有这样的数组:

const one = [
  {id: 1, field1: "a"},
  {id: 2, field1: "b"},
  {id: 3, field1: "c"},
  {id: 4, field1: "d"}
]

const two = [
  {id: 4, field2: "4"},
  {id: 1, field2: "1"}
]
// what I want to achieve:
const result = [
  {id: 1, field1: "a", field2: "1"},
  {id: 4, field1: "d", field2: "4"}
]

我想合并一和二,所以我得到了结果。这与 SQL 连接非常相似,但我想在 JavaScript 代码中完成所有这些操作。注意:

  1. 这是id“列”的“加入”。
  2. 结果的顺序是one数组的顺序(按ID排序只是巧合相同的顺序)
  3. 结果中存在两个数组中的字段

我自己想出了如何做到这一点,但是代码很笨重且难以阅读。基本上,您过滤one 并删除two 中不存在的元素,然后映射one 并合并two 数组的字段。

有没有更简洁的方法来实现这一点?我正在使用 lodash,我希望那里有一个函数可以让这更容易,但我还没有找到。

【问题讨论】:

  • 您肯定可以使用 .reduce 完成此操作。
  • 所以使用JS数据库:npmjs.com/package/database-js
  • 你为什么有field1field2
  • @Justinas 有趣的库,但它似乎与我想要做的事情并不完全一致。如果有的话,那就是矫枉过正了。

标签: javascript typescript lodash


【解决方案1】:

通过键(id)创建第二个数组的字典或Map,然后过滤第一个数组,只保留字典中出现的项目。然后,映射剩余的项目,并从字典中添加相关项目。

const joinBy = (arr1, arr2, key) => {
  const arr2Dict = _.keyBy(arr2, key)
  
  return arr1
    .filter(item => item[key] in arr2Dict)
    .map(item => ({ ...item, ...arr2Dict[item[key]] }))
}

const one = [{id: 1, field1: "a"},{id: 2, field1: "b"},{id: 3, field1: "c"},{id: 4, field1: "d"}]
const two = [{id: 4, field2: "4"},{id: 1, field2: "1"}];

const result = joinBy(one, two, 'id')

console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.min.js" integrity="sha512-90vH1Z83AJY9DmlWa8WkjkV79yfS2n2Oxhsi2dZbIv0nC4E6m5AbH8Nh156kkM7JePmqD6tcZsfad1ueoaovww==" crossorigin="anonymous"></script>

如果你不想使用lodash,你可以轻松替换_.keyBy(),并使用Array.reduce()创建一个对象或者使用Array.map()创建一个Map。

const joinBy = (arr1, arr2, key) => {
  const arr2Dict = new Map(arr2.map(o => [o[key], o]))
  
  return arr1
    .filter(item => arr2Dict.has(item[key]))
    .map(item => ({ ...item, ...arr2Dict.get(item[key]) }))
}

const one = [{id: 1, field1: "a"},{id: 2, field1: "b"},{id: 3, field1: "c"},{id: 4, field1: "d"}]
const two = [{id: 4, field2: "4"},{id: 1, field2: "1"}];

const result = joinBy(one, two, 'id')

console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.min.js" integrity="sha512-90vH1Z83AJY9DmlWa8WkjkV79yfS2n2Oxhsi2dZbIv0nC4E6m5AbH8Nh156kkM7JePmqD6tcZsfad1ueoaovww==" crossorigin="anonymous"></script>

【讨论】:

    【解决方案2】:

    您可以使用来自lodashmergemapfind 函数。用于排序 - sortBy

    _.sortBy(
       _.map(two, (n) => _.merge(n, _.find(one, ['id', n.id]))),
       (o) => _.findIndex(one, ['id', o.id]),
    );
    

    const one = [{id: 1, field1: "a"},{id: 2, field1: "b"},{id: 3, field1: "c"},{id: 4, field1: "d"}], two = [{id: 4, field2: "4"},{id: 1, field2: "1"}];
    
    const r = _.sortBy(
       _.map(two, (n) => _.merge(n, _.find(one, ['id', n.id]))),
       (o) => _.findIndex(one, ['id', o.id]),
    );
    
    console.log(r);
    <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.min.js"></script>

    【讨论】:

    • @DanielKaplan 当然,已修复。
    • 您似乎正在按 ID 排序。就像我说的,这只是巧合我想要的顺序。我想要第一个数组的顺序
    • @DanielKaplan 你明白了。
    【解决方案3】:

    如果您有不同的字段,您可以获取一个带有项目引用的对象并映射two 并按id 排序。

    const
        one = [{ id: 1, field1: "a" }, { id: 2, field1: "b" }, { id: 3, field1: "c" }, { id: 4, field1: "d" }],
        two = [{ id: 4, field2: "4" }, { id: 1, field2: "1" }],
        fromOne = Object.fromEntries(one.map((object, index) => [object.id, { object, index }])),
        result = two
            .map(o => ({ ...fromOne[o.field2].object, ...o }))
            .sort((a, b) => fromOne[a.field2].index - fromOne[b.field2].index);
        
    console.log(result);
    .as-console-wrapper { max-height: 100% !important; top: 0; }

    【讨论】:

    • 您似乎正在按 ID 排序。就像我说的,这只是巧合我想要的顺序。我想要第一个数组的顺序
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-19
    • 1970-01-01
    • 2020-12-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多