Reputation: 27
What is the best way to replace a string in an array, with an array from another array based on their matching values? I have been grinding my teeth for nearly a week on this, but no luck so far..
Say we have these 3 arrays in JavaScript:
// Array with list of content:
[
[
{id: "1-AAA", content: "foo"},
{id: "1-AAA", content: "foo"},
{id: "1-AAA", content: "bar"},
{id: "1-AAA", content: "baz"},
{id: "1-AAA", content: "baz"}
],
[
{id: "1-CCC", content: "bar"}
],
[
{id: "1-DDD", content: "foo"}
],
[
{id: "2-AAA", content: "foo"},
{id: "2-AAA", content: "bar"},
{id: "2-AAA", content: "bar"}
]
]
// Array 1 with unique id's and last known content:
[
{id: "1-AAA", content: "baz"},
{id: "1-BBB", content: undefined},
{id: "1-CCC", content: "bar"},
{id: "1-DDD", content: "foo"},
{id: "1-EEE", content: undefined}
]
// Array 2 with unique id's and last known content:
[
{id: "2-AAA", content: "bar"},
{id: "2-BBB", content: undefined},
{id: "2-CCC", content: undefined},
{id: "2-DDD", content: undefined},
{id: "2-EEE", content: undefined}
]
How can I assign the content values from the first array as an array of content to the specific id's (see code snippets below)? Preferably it should only have the unique values (f.e. by using [...new Set(content)]).
// Intended result for array 1:
[
{
id: "1-AAA",
content: ["foo", "bar", "baz"]
},
{
id: "1-CCC",
content: ["bar"]
},
{
id: "1-DDD",
content: ["foo"]
}
]
// Intended result for array 2:
[
{
id: "2-AAA",
content: ["foo", "bar"]
}
]
I tried many different things so far, but can't find a good solution for reassigning the nested arrays.. Any help would be much appreciated! Thanks in advance guys!
Upvotes: 0
Views: 195
Reputation: 50694
You can start off by creating a look up table to make it easy to find content arrays based on an id:
"1-AAA": ["foo", "foo", "bar", "baz", "baz"],
"1-CCC": ["bar"],
"1-DDD": ["foo"],
"2-AAA": ["foo", "bar", "bar"]
Below I'm using a function called transform()
to do this which builds a Map of the above shape from your first array.
Then you can filter your array to remove any objects which have a content of undefined
using the .filter()
method. Lastly, you can .map()
each object to a new object, where each new object has the original object's properties (this is done using the spread syntax) with a modified content
property. To modify the content you can use the above lookup table to obtain the array of "contents", as well as append the current object's content value. You can then wrap this in a Set to remove any duplicates:
const arr = [ [ {id: "1-AAA", content: "foo"}, {id: "1-AAA", content: "foo"}, {id: "1-AAA", content: "bar"}, {id: "1-AAA", content: "baz"}, {id: "1-AAA", content: "baz"} ], [ {id: "1-CCC", content: "bar"} ], [ {id: "1-DDD", content: "foo"} ], [ {id: "2-AAA", content: "foo"}, {id: "2-AAA", content: "bar"}, {id: "2-AAA", content: "bar"} ] ];
const transform = arr => new Map(arr.map(inner => {
const [{id}] = inner;
return [id, inner.map(({content}) => content)];
}));
const replaceObj = (map, arr) => arr
.filter(({content}) => content)
.map(o => ({...o, content: [...new Set([...map.get(o.id), o.content])]}));
const lookup = transform(arr);
console.log(replaceObj(lookup, [{id: "1-AAA", content: "baz"}, {id: "1-BBB", content: undefined}, {id: "1-CCC", content: "bar"}, {id: "1-DDD", content: "foo"}, {id: "1-EEE", content: undefined}]));
console.log(replaceObj(lookup, [{id: "2-AAA", content: "bar"}, {id: "2-BBB", content: undefined}, {id: "2-CCC", content: undefined}, {id: "2-DDD", content: undefined}, {id: "2-EEE", content: undefined}]));
You could also instead make a new function that returns a new function that closes over the transformed version of your array so you don't need to keep passing in the transformed array (ie: the Map) into your function:
const arr = [ [ {id: "1-AAA", content: "foo"}, {id: "1-AAA", content: "foo"}, {id: "1-AAA", content: "bar"}, {id: "1-AAA", content: "baz"}, {id: "1-AAA", content: "baz"} ], [ {id: "1-CCC", content: "bar"} ], [ {id: "1-DDD", content: "foo"} ], [ {id: "2-AAA", content: "foo"}, {id: "2-AAA", content: "bar"}, {id: "2-AAA", content: "bar"} ] ];
const getReplacer = arr => {
const map = new Map(arr.map(inner => {
const [{id}] = inner;
return [id, inner.map(({content}) => content)];
}));
return arr => arr.filter(({content}) => content)
.map(o => ({...o, content: [...new Set([...map.get(o.id), o.content])]}));
}
const replaceObj = getReplacer(arr);
console.log(replaceObj([{id: "1-AAA", content: "baz"}, {id: "1-BBB", content: undefined}, {id: "1-CCC", content: "bar"}, {id: "1-DDD", content: "foo"}, {id: "1-EEE", content: undefined}]));
console.log(replaceObj([{id: "2-AAA", content: "bar"}, {id: "2-BBB", content: undefined}, {id: "2-CCC", content: undefined}, {id: "2-DDD", content: undefined}, {id: "2-EEE", content: undefined}]));
You could also use a method such as .find()
on your first array instead of creating a lookup table and using .get()
, but this would make things inefficient as your array sizes grow. Using a lookup table thus helps with efficiency.
Upvotes: 1