How to combine two array using ramda library? - javascript

I have two array as below
const l1 = [ {"a": 1, "a1": 2}, {"b": 3, "b1": 4}, {"c": 5, "c1": 6} ];
const l2 = [ {"d": 2}, {"e": 2}, {"f": 2} ];
How can I use ramda library to combine these two and get result like
[ {"a": 1, "a1": 2, "d": 2}, {"a": 1, "a1": 2, "e": 2}, {"a": 1, "a1": 2, "f": 2},
{"b": 3, "b1": 4, "d": 2}, {"b": 3, "b1": 4, "e": 2}, {"b": 3, "b1": 4, "f": 2},
{"c": 5, "c1": 6, "d": 2}, {"c": 5, "c1": 6, "e": 2}, {"c": 5, "c1": 6, "f": 2} ]
I used
R.map(R.xprod(__, l2, l1)
But not working since i got object inside array.
Thanks in advance.

To get a Cartesian product of two arrays of objects you can create a function by lifting merge:
const fn = R.lift(R.merge)
const l1 = [ {"a": 1, "a1": 2}, {"b": 3, "b1": 4}, {"c": 5, "c1": 6} ];
const l2 = [ {"d": 2}, {"e": 2}, {"f": 2} ];
const result = fn(l1, l2)
console.log(result)
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.0/ramda.js" integrity="sha256-buL0byPvI/XRDFscnSc/e0q+sLA65O9y+rbF+0O/4FE=" crossorigin="anonymous"></script>

If not very particular about ramda, you can do as below with one line using flatMap and map.
const l1 = [
{ a: 1, a1: 2 },
{ b: 3, b1: 4 },
{ c: 5, c1: 6 }
];
const l2 = [{ d: 2 }, { e: 2 }, { f: 2 }];
const merged = l2.flatMap(item2 => l1.map(item1 => ({ ...item1, ...item2 })));
console.log(JSON.stringify(merged));

With plain Javascript, you could take cartesian product with a double nested reduce by mapping new objects.
This approach works for an arbirary count of arrays (sort of ...).
const
l1 = [{ a: 1, a1: 2 }, { b: 3, b1: 4 }, { c: 5, c1: 6 }],
l2 = [{ d: 2 }, { e: 2 }, { f: 2 }],
result = [l1, l2].reduce((a, b) => a.reduce((r, v) => r.concat(b.map(w => ({ ...v, ...w }))), []));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

There is another option to merge arrays using R.reduce function
const input = {
arr1: ['a', 'b'],
arr2: ['c', 'd']
}
R.compose(
R.reduce((acc, cur) => {
return [...acc, ...cur]
}, []),
R.map(([key, value]) => value),
R.toPairs
)(input)

Related

map array of objects based on set of properties

Suppose I have an object:
let array = [
{a: 1, b: 5, c: 9},
{a: 2, b: 6, c: 10},
{a: 3, b: 7, c: 11},
{a: 4, b: 8, c: 12}
];
then I have a dictionary:
const columns = [
{ key: 'a', value: 'a' },
{ key: 'b', value: 'b' },
]
I want to filter out properties that are not defined in columns.
I have tried
array.map((x) => ({"a": x.a, "b": x.b}))
Is there a way to use the data defined in columns instead of manually typing all the properties?
Desired output:
[
{
"a": 1,
"b": 5
},
{
"a": 2,
"b": 6
},
{
"a": 3,
"b": 7
},
{
"a": 4,
"b": 8
}
]
You could map entries and get the new objects.
let
array = [{ a: 1, b: 5, c: 9 }, { a: 2, b: 6, c: 10 }, { a: 3, b: 7, c: 11 }, { a: 4, b: 8, c: 12 }],
columns = [{ key: 'a', value: 'a' }, { key: 'b', value: 'b' }],
keys = columns.map(({ key }) => key),
result = array.map(o => Object.fromEntries(keys.map(k => [k, o[k]])));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You could use this.
This uses just an array to hold the desired columns because I don't get why you would use a dictionary with key and value being the same.
let array = [
{ a: 1, b: 5, c: 9 },
{ a: 2, b: 6, c: 10 },
{ a: 3, b: 7, c: 11 },
{ a: 4, b: 8, c: 12 },
];
const desiredColumns = ["a", "b"];
const transformed = array.map(item => {
const obj = {};
desiredColumns.forEach(col => {
if(col in item){
obj[col] = item[col];
}
})
return obj;
})
console.log(array);
console.log(transformed)
Another, slightly less direct way using map() and reduce():
Create an array with all the keys we'll keep
Reduce the array to get the desired result
Add current key + value if key keep array
const array = [{a: 1, b: 5, c: 9}, {a: 2, b: 6, c: 10}, {a: 3, b: 7, c: 11}, {a: 4, b: 8, c: 12} ];
const columns = [{ key: 'a', value: 'a' }, { key: 'b', value: 'b' }, ];
const toKeep = columns.map(({ key }) => key).flat();
const result = array.map(a =>
Object.keys(a)
.reduce((prev, cur) => (toKeep.includes(cur)) ? { ...prev, [cur]: a[cur] } : prev, {})
);
console.log(result);
Result:
[
{
"a": 1,
"b": 5
},
{
"a": 2,
"b": 6
},
{
"a": 3,
"b": 7
},
{
"a": 4,
"b": 8
}
]

push the contents of array into another array without looping

Here's a simple piece of JavaScript where I want to add the contents of orders.foo and orders2.foo to a single-dimensional ordersArr.
let _ = require('underscore');
let ordersArr = [];
let orders = {
foo: [
{x: 1, b: 2},
{y: 1, c: 3},
{a: 2, d: 4}
]
}
ordersArr = _.map(orders.foo, order => order)
orders2 = {
foo: [
{x: 2, b: 3},
{y: 5, c: 4},
{a: 3, d: 6}
]
}
let tOrders = _.map(orders2.foo, order => order);
ordersArr.push(tOrders)
console.log(ordersArr);
The problem with this code is that push in this case creates a multi-dimensional array:
Output
[
{ x: 1, b: 2 },
{ y: 1, c: 3 },
{ a: 2, d: 4 },
[ { x: 2, b: 3 }, { y: 5, c: 4 }, { a: 3, d: 6 } ]
]
How do I iterate the contents of orders.foo and orders2.foo and have their results as one single dimension array?
You can spread the content of both arrays into the new array
const arr1 = [1,2,3];
const arr2 = [4,5,6];
const arr3 = [...arr1, ...arr2];
console.log(arr3);
// prints [1,2,3,4,5,6]
Spreading arr2 into arr1 also works.
arr1.push(...arr2);
console.log(arr1);
// prints [1,2,3,4,5,6]
So changing
ordersArr.push(tOrders)
to
ordersArr.push(...tOrders);
should work.
For a full answer:
let ordersArr = [];
let orders = {
foo: [
{x: 1, b: 2},
{y: 1, c: 3},
{a: 2, d: 4}
]
}
orders2 = {
foo: [
{x: 2, b: 3},
{y: 5, c: 4},
{a: 3, d: 6}
]
}
ordersArr.push(...orders.foo, ...orders2.foo);
Using underscore _.flatten:
const
orders = { foo: [ {x: 1, b: 2}, {y: 1, c: 3}, {a: 2, d: 4} ] },
orders2 = { foo: [ {x: 2, b: 3}, {y: 5, c: 4}, {a: 3, d: 6} ] };
const ordersArr = _.flatten([orders.foo, orders2.foo]);
console.log(ordersArr);
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.2/underscore-min.js"></script>
Using javascript spread operator:
const
orders = { foo: [ {x: 1, b: 2}, {y: 1, c: 3}, {a: 2, d: 4} ] },
orders2 = { foo: [ {x: 2, b: 3}, {y: 5, c: 4}, {a: 3, d: 6} ] };
const ordersArr = [...orders.foo, ...orders2.foo];
console.log(ordersArr);
Using javascript Array#concat:
const
orders = { foo: [ {x: 1, b: 2}, {y: 1, c: 3}, {a: 2, d: 4} ] },
orders2 = { foo: [ {x: 2, b: 3}, {y: 5, c: 4}, {a: 3, d: 6} ] };
const ordersArr = orders.foo.concat(orders2.foo);
console.log(ordersArr);
The spread operator mentioned above is the best 2021 way to do it.
let ordersArr = [...orders.foo, ...orders2.foo];
Use Array.concat()
let orders1 = {
foo: [
{x: 1, b: 2},
{y: 1, c: 3},
{a: 2, d: 4}
]
};
let orders2 = {
foo: [
{x: 2, b: 3},
{y: 5, c: 4},
{a: 3, d: 6}
]
};
console.log( orders1.foo.concat(orders2.foo) );
You can use concat() to merge the arrays and create a single new array:
let tOrders = orders.foo.concat(orders2.foo);
let ordersArr = [];
let orders = {
foo: [
{x: 1, b: 2},
{y: 1, c: 3},
{a: 2, d: 4}
]
}
ordersArr = _.map(orders.foo, order => order)
orders2 = {
foo: [
{x: 2, b: 3},
{y: 5, c: 4},
{a: 3, d: 6}
]
}
let tOrders = orders.foo.concat(orders2.foo);
console.log(tOrders)
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.2/underscore-min.js"></script>
Another option using flat()
let ordersArr = [];
let orders = {
foo: [
{x: 1, b: 2},
{y: 1, c: 3},
{a: 2, d: 4}
]
}
orders2 = {
foo: [
{x: 2, b: 3},
{y: 5, c: 4},
{a: 3, d: 6}
]
}
let tOrders = [orders.foo, orders2.foo].flat();
console.log(tOrders)
Immutable merge of arrays
Creates a new array.
Merge using the spread operator
let orders = {
foo: [
{x: 1, b: 2},
{y: 1, c: 3},
{a: 2, d: 4}
]
}
let orders2 = {
foo: [
{x: 2, b: 3},
{y: 5, c: 4},
{a: 3, d: 6}
]
}
const mergeResult = [...orders.foo, ...orders2.foo];
console.log(mergeResult);
Merge using array.concat() method
let orders = {
foo: [
{x: 1, b: 2},
{y: 1, c: 3},
{a: 2, d: 4}
]
}
let orders2 = {
foo: [
{x: 2, b: 3},
{y: 5, c: 4},
{a: 3, d: 6}
]
}
const mergeResult = orders.foo.concat(orders2.foo);
console.log(mergeResult);
Mutable merge of arrays
Merge it into existing array.
Merge using array.push() method
let orders = {
foo: [
{x: 1, b: 2},
{y: 1, c: 3},
{a: 2, d: 4}
]
}
let orders2 = {
foo: [
{x: 2, b: 3},
{y: 5, c: 4},
{a: 3, d: 6}
]
}
orders.foo.push(...orders2.foo);
console.log(orders.foo);
I'll add one more flavor to the list. You can create a shallow copy of an array using the built-in slice method, which has been with us for a very long time:
var ordersArr = orders.foo.slice();
Now you can add the contents of the other array using push and apply:
ordersArr.push.apply(ordersArr, orders2.foo);
Et voilá, ordersArr is now a one-dimensional array containing all elements of both orders.foo and orders2.foo. Works even in ES3!
For inspiration, you can find lots of nice little tricks like this in the Underscore source code.
i think this will work for you.
let tOrders = _.map(orders2.foo, order => order);
tOrders.foreach((element)=>{
ordersArr.push(element)
})
console.log(ordersArr);

Get only some keys of object of objects

I've this object:
const dataset = {
"2019": {
"a": 1,
"b": 2,
"c": 3,
"d": 4
},
"2020": {
"a": 2,
"b": 4,
"c": 6,
"d": 8
},
"2021": {
"a": 10,
"b": 11,
"c": 12,
"d": 13
}
}
I would like to obtain these two objects:
const obj1 = {
"2019": {
"a": 1,
"c": 3,
},
"2020": {
"a": 2,
"c": 6,
},
"2021": {
"a": 10,
"c": 12,
}
}
const obj2 = {
"2019": {
"b": 2,
"d": 4
},
"2020": {
"b": 4,
"d": 8
},
"2021": {
"b": 11,
"d": 13
}
}
So "split" the object in two objects based on some keys of the inner objects.
Here is what I tried:
function pickKeys(dataObj, keys) {
return Object.entries(dataObj).map(([d, obj]) => {
return { [d]: _.pick(obj, keys) }
})
}
const obj1 = pickKeys(dataset, ['a', 'c'])
The result is:
const obj1 = [
{ '2019': { a: 1, c: 3 } },
{ '2020': { a: 2, c: 6 } },
{ '2021': { a: 10, c: 12 } }
]
So almost there but it's not perfect. Which is the better way to do that?
You do this using combination of map, reduce methods and one for...in loop that will turn array of keys into array of objects. Then you can use array destructuring to get two separate objects.
const dataset = {"2019":{"a":1,"b":2,"c":3,"d":4},"2020":{"a":2,"b":4,"c":6,"d":8},"2021":{"a":10,"b":11,"c":12,"d":13}}
const [a, b] = [['a', 'c'], ['b', 'd']]
.map(keys => keys.reduce((r, key) => {
for (let year in dataset) {
if (!r[year]) r[year] = {}
r[year][key] = dataset[year][key]
}
return r;
}, {}))
console.log(a)
console.log(b)
The problem is that map returns an array with replaced elements, while you want an object.
Since you are already using Lodash you could use mapValues to transform the values of an object and return an object instead of an array.
function pickKeys(dataObj, keys) {
return _.mapValues(dataObj, obj => _.pick(obj, keys));
}
function pickKeys(dataObj, keys) {
return _.mapValues(dataObj, obj => _.pick(obj, keys));
}
const dataset = {
"2019": {
"a": 1,
"b": 2,
"c": 3,
"d": 4
},
"2020": {
"a": 2,
"b": 4,
"c": 6,
"d": 8
},
"2021": {
"a": 10,
"b": 11,
"c": 12,
"d": 13
}
}
console.log(pickKeys(dataset, ["a", "c"]));
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.15/lodash.min.js"></script>
You could map the wanted keys by using the entries of the given object.
const
dataset = { 2019: { a: 1, b: 2, c: 3, d: 4 }, 2020: { a: 2, b: 4, c: 6, d: 8 }, 2021: { a: 10, b: 11, c: 12, d: 13 } },
groups = [['a', 'c'], ['b', 'd']],
[result1, result2] = Object
.entries(dataset)
.reduce((r, [k, o]) =>
groups.map((group, i) =>
group.reduce(
(q, g) => ({ ...q, [k]: { ...q[k], [g]: o[g] } }),
r[i] || {}
)
),
[]
);
console.log(result1);
console.log(result2);
.as-console-wrapper { max-height: 100% !important; top: 0; }
The next provided example code is reduce based, generic but configurable in its usage ...
function createAndCollectSubdata(collector, dataEntry) {
const { keyLists, subdataList } = collector;
const [ dataKey, dataValue ] = dataEntry;
keyLists.forEach((keyList, idx) => {
const data = subdataList[idx] || (subdataList[idx] = {});
const subdata = data[dataKey] || (data[dataKey] = {}) ;
keyList.forEach(key => subdata[key] = dataValue[key]);
});
return collector;
}
const dataset = {
"2019": {
"a": 1,
"b": 2,
"c": 3,
"d": 4
},
"2020": {
"a": 2,
"b": 4,
"c": 6,
"d": 8
},
"2021": {
"a": 10,
"b": 11,
"c": 12,
"d": 13
}
};
const [
acSubdata,
bdSubdata
] = Object.entries(dataset).reduce(createAndCollectSubdata, {
keyLists: [["a", "c"], ["b", "d"]],
subdataList: []
}).subdataList;
console.log('acSubdata :', acSubdata);
console.log('bdSubdata :', bdSubdata);
.as-console-wrapper { min-height: 100%!important; top: 0; }

Javascript lodash merge array items by first item

there is a way for merge array items by first item
_.merge({a: 1, c: [{y: 8}, {z: 9}]}, {b: 0, c: [{x: 5}]})
Result:
{
"a": 1,
"c": [
{
"y": 8,
"x": 5
},
{
"z": 9
}
],
"b": 0
}
what i want:
{
"a": 1,
"c": [
{
"y": 8,
"x": 5
},
{
"z": 9,
"x": 5 // <-------------------------
}
],
"b": 0
}
I want merge a source object by another used like a model. In case of array the model define only the first item of the collection and the source object should reflect the first model item into all the collection items.
I wouldn't actually do it, as it might be very confusing (especially when you've got an array with more than 1 item).
You can use _.mergeWith(), and manually iterate and merge array items.
const mrg = (o1, o2) => _.mergeWith(
o1, o2,
(a, b) => _.isArray(a) && _.isArray(b) ?
a.map((item, i) => mrg(item, b[Math.min(i, b.length - 1)]))
:
undefined
)
const result = mrg({a: 1, c: [{y: 8}, {z: 9}]}, {b: 0, c: [{x: 5}]})
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.14/lodash.js"></script>
Use _.mergeWith
function customizer(objValue, srcValue) {
if (_.isArray(objValue) && _.every(objValue, _.isObject) &&
_.isArray(srcValue) && _.every(srcValue, _.isObject) )
{
let newObj = Object.assign(...srcValue)
_.forEach(objValue, function(obj, index) {
objValue[index] = Object.assign(obj, newObj)
});
}
}
let a = {a: 1, c: [{y: 8}, {z: 9}]}
let b = {b: 0, c: [{x: 5}]}
let res = _.mergeWith(a, b, customizer)
Result:
{
"a": 1,
"c": [
{
"y": 8,
"x": 5
},
{
"z": 9,
"x": 5
}
],
"b": 0
}

Filtering JSON Data based on the Values in the Array

In Javascript, is there a way to filter the JSON file based on the values in the array?
For example, with the following array:
["c", "f"]
and the JSON object file:
[{
"a": 1,
"b": 2,
"c": 3,
"d": 4,
"e": 5,
"f": 6
},{
"a": 2,
"b": 4,
"c": 6,
"d": 8,
"e": 10,
"f": 12
}]
I would like to generate the following result:
[{
"c": 3,
"f": 6
},{
"c": 6,
"f": 12
}]
You could map the values of the given keys for a new object.
var keys = ["c", "f"],
data = [{ a: 1, b: 2, c: 3, d: 4, e: 5, f: 6 }, { a: 2, b: 4, c: 6, d: 8, e: 10, f: 12 }],
filtered = data.map(o => Object.assign(...keys.map(k => ({ [k]: o[k] }))));
console.log(filtered);
You can use map() and reduce() for this.
var keys = ["c", "f"];
var arr = [{"a":1,"b":2,"c":3,"d":4,"e":5,"f":6},{"a":2,"b":4,"c":6,"d":8,"e":10,"f":12}];
var result = arr.map(o => {
return keys.reduce((r, k) => (o[k] && (r[k] = o[k]), r), {})
})
console.log(result)

Categories

Resources