3

I have arrays like this:

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"}
]

I want to merge one and two, so I get the result. This is very similar to a SQL join, but I want to do all of this in JavaScript code. Notice:

  1. This is "joining" by the id "column."
  2. The order of the result is the order of the one array (sorting by ID is only coincidentally the same order)
  3. Fields from both arrays exist on the result

I've figured out how to do this on my own, but the code is clunky and difficult to read. Basically, you filter over one and remove elements that don't exist in two, then you map over one and merge the fields of the two array.

Is there a more concise way of achieving this? I'm using lodash, and I was hoping there was a function in there to make this easier, but I haven't found any.

6
  • You can accomplish this with .reduce for sure. Commented Oct 16, 2020 at 18:45
  • So use JS database: npmjs.com/package/database-js Commented Oct 16, 2020 at 18:45
  • 1
    Your answer is here stackoverflow.com/a/17504827/8683285 Commented Oct 16, 2020 at 18:46
  • why do you have field1 and field2? Commented Oct 16, 2020 at 18:54
  • @Justinas interesting library, but it doesn't seem to completely align with what I'm trying to do. If anything, it's overkill. Commented Oct 16, 2020 at 19:03

3 Answers 3

4

Create a dictionary or Map of the 2nd array by the key (id), and then filter the 1st array, and keep only items that appear in the dictionary. Afterwards, map the remaining items, and add the relevant items from the dictionary.

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>

If you don't want to use lodash, you can easily replace _.keyBy(), and create an object using Array.reduce() or create a Map with Array.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>

Sign up to request clarification or add additional context in comments.

Comments

2

You can use merge, map and find functions from lodash. For sorting - 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>

3 Comments

@DanielKaplan Sure, fixed.
it looks like you are sorting by ID. Like I said, this is only coincidentally the order that i want. I want the order of the first array
@DanielKaplan You got it.
0

If you have different fields, you could take an object with the reference to the items and map two and sort by 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; }

1 Comment

it looks like you are sorting by ID. Like I said, this is only coincidentally the order that i want. I want the order of the first array

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.