Lodash Merge not updating object with correct values - arrays

I am trying to merge these two objects using Lodash merge.
Server object:
[
{
"id": "74738",
"customerId": "534430"
},
{
"id": "74742",
"customerId": "534429"
}
]
Local Object
[
{
"customerId": "534429"
"name": "ABC"
},
{
"customerId": "534430",
"name": "XYZ"
},
]
I am using lodash merge to combine these two objects based on attributes, I am using this code below:
merge({}, serverObject, localObject);
// Output: [{"id":"74738","customerId":"534429","name":"ABC"},{"id":"74742","customerId":"534430","name":"XYZ"}]
The object is not being updated based on the Customer Ids but instead by the sequence of local object.
Expected Output:
[{"id":"74738","customerId":"534430","name":"ABC"},{"id":"74742","customerId":"534429","name":"XYZ"}]

With lodash you can combine all arrays to a single one using _.flatten(), group by a predicate, and then map and merge each group to a single object.
Note: since your customerId is an integer, the items would be ordered by the numerical value.
const { map, groupBy, flatten, merge } = _
const fn = (predicate, ...arrs) => map(
groupBy(flatten(arrs), predicate),
group => merge({}, ...group)
)
const arr1 = [{"id":"74738","customerId":"534430"},{"id":"74742","customerId":"534429"}]
const arr2 = [{"customerId":"534429","name":"ABC"},{"customerId":"534430","name":"XYZ"}]
const result = fn('customerId', arr1, arr2)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js" integrity="sha512-WFN04846sdKMIP5LKNphMaWzU7YpMyCU245etK3g/2ARYbPK9Ub18eG+ljU96qKRCWh+quCY7yefSmlkQw1ANQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
If you need to preserve the original order, flatten to a single array, and then reduce the items to a Map using the result of the predicate as the key. Merge each new item with the same key to the existing one in the Map. Convert the Map's values back to an array using Array.from():
const fn = (predicate, ...arrs) => Array.from(arrs
.flat()
.reduce((acc, o) => {
const key = predicate(o)
return acc.set(key, { ...acc.get(key), ...o })
}, new Map()).values()
)
const arr1 = [{"id":"74738","customerId":"534430"},{"id":"74742","customerId":"534429"}]
const arr2 = [{"customerId":"534429","name":"ABC"},{"customerId":"534430","name":"XYZ"}]
const result = fn(o => o.customerId, arr1, arr2)
console.log(result)

Related

What is best way of finding array element intersection on rxjs

I know There is includes Operator on JavaScript Array.
So, Finding common elements from two other Arrays is no Problem. (https://stackoverflow.com/a/1885569/11455650)
Then, What is best way for this on rxjs?
const obs1$ = from(["foo", "bar", "baz", "qux"]);
const obs2$ = from(["bar", "garply", "fred", "foo"];
// const commonIntersection$ = functions or operators...
// result must be ["foo", "bar"]
I think there are two way for implementing this.
Which one is computationally efficient and How can I implement this with rxjs Operator?
merge Two Array and emit only Second value (ignore First Value)
filter each of elements
I assume you want a running intersection from each emission? If so you can either use the scan operator, or roll your own specific intersection operator.
The scan operator is like the reduce method on arrays. In this case, for each element (which is an array of strings) the intersection is returned. Each following emission will work on that last result.
merge(from([["foo", "bar", "baz", "qux"], ["bar", "garply", "fred", "foo"]])).pipe(
map(x => x),
scan((acc, cur) => {
return (!acc)
? next
: acc.filter(x => cur.includes(x));
}, undefined as string[] | undefined),
).subscribe(x => console.log(x));
A custom operator will look cleaner, so if you need it multiple times, then go for it! As you can see the logic below is essentially the same as the scan version. It keeps track of the running intersection state in the acc variable, and the current array is used to update it.
merge(from([["foo", "bar", "baz", "qux"], ["bar", "garply", "fred", "foo"]])).pipe(
map(x => x),
intersection(),
).subscribe(x => console.log(x));
/*...*/
function intersection<T>(): OperatorFunction<T[], T[]> {
return (observable: Observable<T[]>) => {
return new Observable<T[]>((subscriber) => {
let acc: T[] | undefined; // keeps track of the state.
const subscription = observable.subscribe({
next: (cur) => {
acc = (!acc)
? cur
: acc.filter(x => cur.includes(x));
subscriber.next(acc);
},
error: (err) => subscriber.error(err),
complete: () => subscriber.complete(),
});
return () => subscription.unsubscribe();
})
}
}
If you only want the last result then change the scan operator to reduce, or only emit the value of acc in the complete callback of the inner subscription in the operator version.
import { forkJoin, from } from 'rxjs';
import { toArray } from 'rxjs/operators';
import { intersection } from 'lodash';
const obs1$ = from(['foo', 'bar', 'baz', 'qux']);
const obs2$ = from(['bar', 'garply', 'fred', 'foo']);
forkJoin([obs1$.pipe(toArray()), obs2$.pipe(toArray())]).subscribe(
([arr1, arr2]) => console.log(intersection(arr1, arr2))
);
Try here: https://stackblitz.com/edit/rxjs-6jdyzy?file=index.ts
This is not rxjs used codes but used JavaScript Native Array methods (filter, includes).
It seems faster than rxjs operator.
const intersectionArray = combineLatest({
firstArray: [["foo", "bar", "baz", "qux"]],
secondArray: [["bar", "garply", "fred", "foo"]],
}).pipe(
map((x) => x.firstArray.filter((value) => x.secondArray.includes(value)))
);
intersectionArray.subscribe(console.log);

Creating a nested json using two arrays in node js

I have two arrays one with field names and the other array is having a list of arrays where each array corresponds to row in table.How can i use these two arrays to create a list of JSON Objects. Is there any in built function. I am able to acheive this using map/reduce/ for -loop but this is impacting the performance if the second array is having more rows as we have traverse through each row.
I hope the following explains the use case better. Please share the sample code if possible.
Arr1=[field1,field2];
Arr2=[[1,2],[3,4],[5,6]];
Expected Output:
[
{
field1 :1 ,
field2: 2
},
{
field1 :3 ,
field2: 4
},
{
field1 :5 ,
field2: 6
}
]
You can use Array.map to map the elements
const mapFields = (arr1, arr2) => {
const mappedArray = arr2.map((el) => {
const mappedArrayEl = [];
el.forEach((value, i) => {
if (arr1.length < (i+1)) return;
mappedArrayEl[arr1[i]] = value;
});
return mappedArrayEl;
});
return mappedArray;
}
const Arr1 = ["field1","field2"];
const Arr2 = [[1,2],[3,4],[5,6]];
console.log(mapFields(Arr1, Arr2));

Loop and merge object in Lodash

I have a array of object, like this:
var obj = [{
employeeId: 1300000,
requestValue: 2
}, {
employeeId: 1300000,
requestValue: 3
}, {
employeeId: 1300001,
requestValue: 4
}]
I know how to do it in javascript. But, How can I a retrieve the following result using Lodash:
var result = {
1300000: [
{ requestValue: 2 },
{ requestValue: 3 }
],
1300001: [
{ requestValue: 4 }
]
}
Using Lodash:
Group By with employeeId
mapValues to map each group, and take only requestValue by using
map
Here is the example:
let input = [{"employeeId":1300000,"requestValue":2},{"employeeId":1300000,"requestValue":3},{"employeeId":1300001,"requestValue":4}],
res = _(input)
.groupBy('employeeId')
.mapValues(g => _.map(g, ({requestValue}) => ({requestValue})))
.value();
console.log(res)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
Using Plain JavaScript
Take a hash (resulant) and start with iterating it using reduce
Check if the employee id exists as a key of the hash for each
employeeId, if exist use it otherwise create the key value pair with
a empty array
Take either the existing value (for the key) or the new one and push
requestValue
let input = [{"employeeId":1300000,"requestValue":2},{"employeeId":1300000,"requestValue":3},{"employeeId":1300001,"requestValue":4}],
res = input.reduce((r, {employeeId, requestValue})=> {
(r[employeeId] = r[employeeId] || []).push({requestValue});
return r;
}, {});
console.log(res);

Remove duplicate content from array

"name": [
{
"name": "test1"
},
{
"name": "test2"
},
{
"name": "test3"
},
{
"name": "test1"
},
]
I have the above created by nodejs. During array push, I would like to remove duplicated arrays from the list or only push the name array if the individual array does not exist.
I have tried below codes but it changes the array.
var new = [];
for (var i =0;i<name.length;i++){
new['name'] = name[i].name;
}
The easiest way is probably with Array.prototype.reduce. Something along these lines, given your data structure:
obj.name = Object.values(obj.name.reduce((accumulator, current) => {
if (!accumulator[current.name]) {
accumulator[current.name] = current
}
return accumulator
}, {}));
The reduce creates an object that has keys off the item name, which makes sure that you only have unique names. Then I use Object.values() to turn it back into a regular array of objects like in your data sample.
the solution can be using temp Set;
const tmpSet = new Set();
someObj.name.filter((o)=>{
const has = tmpSet.has(o.name);
tmp.add(o.name);
return has;
});
the filter function iterating over someObj.name field and filter it in place if you return "true". So you check if it exists in a tmp Set & add current value to Set to keep track of duplicates.
PS: new is a reserved word in js;
This should do it
const names = ['John', 'Paul', 'George', 'Ringo', 'John'];
let unique = [...new Set(names)];
console.log(unique); // 'John', 'Paul', 'George', 'Ringo'
https://wsvincent.com/javascript-remove-duplicates-array/

ES6: Merge two arrays into an array of objects

I have two arrays that I want to merge together to one array of objects...
The first array is of dates (strings):
let metrodates = [
"2008-01",
"2008-02",
"2008-03",..ect
];
The second array is of numbers:
let figures = [
0,
0.555,
0.293,..ect
]
I want to merge them to make an object like this (so the array items match up by their similar index):
let metrodata = [
{data: 0, date: "2008-01"},
{data: 0.555, date: "2008-02"},
{data: 0.293, date: "2008-03"},..ect
];
So far I do this like so: I create an empty array and then loop through one of the first two arrays to get the index number (the first two arrays are the same length)... But is there an easier way (in ES6)?
let metrodata = [];
for(let index in metrodates){
metrodata.push({data: figures[index], date: metrodates[index]});
}
The easiest way is probably to use map and the index provided to the callback
let metrodates = [
"2008-01",
"2008-02",
"2008-03"
];
let figures = [
0,
0.555,
0.293
];
let output = metrodates.map((date,i) => ({date, data: figures[i]}));
console.log(output);
Another option is to make a generic zip function which collates your two input arrays into a single array. This is usually called a "zip" because it interlaces the inputs like teeth on a zipper.
const zip = ([x,...xs], [y,...ys]) => {
if (x === undefined || y === undefined)
return [];
else
return [[x,y], ...zip(xs, ys)];
}
let metrodates = [
"2008-01",
"2008-02",
"2008-03"
];
let figures = [
0,
0.555,
0.293
];
let output = zip(metrodates, figures).map(([date, data]) => ({date, data}));
console.log(output);
Another option is to make a generic map function which accepts more than one source array. The mapping function will receive one value from each source list. See Racket's map procedure for more examples of its use.
This answer might seem the most complicated but it is also the most versatile because it accepts any number of source array inputs.
const isEmpty = xs => xs.length === 0;
const head = ([x,...xs]) => x;
const tail = ([x,...xs]) => xs;
const map = (f, ...xxs) => {
let loop = (acc, xxs) => {
if (xxs.some(isEmpty))
return acc;
else
return loop([...acc, f(...xxs.map(head))], xxs.map(tail));
};
return loop([], xxs);
}
let metrodates = [
"2008-01",
"2008-02",
"2008-03"
];
let figures = [
0,
0.555,
0.293
];
let output = map(
(date, data) => ({date, data}),
metrodates,
figures
);
console.log(output);
If you use lodash, you can use zipWith + ES6 shorthand propery names + ES6 Arrow functions for a one-liner, otherwise see #noami's answer.
const metrodata = _.zipWith(figures, metrodates, (data, date)=> ({ data, date }));

Resources