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
}
Related
I want to be able to sort the nested object below from the lowest number to the high number...
{
"a": 50,
"b": {
"c": {
"d": 69,
"e": 420,
"f": 21,
"g": {
"h": 5,
"i": 3,
}
}
},
"j": 1,
"k": 1000
}
... so that it can look like this:
{
"j": 1, // min in OBJECT
"b": {
"c": { // min in B
"g": { // min in C
"i": 3, // min in G
"h": 5
},
"f": 21,
"d": 69,
"e": 420
}
},
"a": 50,
"k": 1000
}
Note that the object is sorted by the lowest number inside objects, meaning the object's direct children's values are [1, 3, 50, 1000].
Quick and dirty using Object.entries, Array forEach, Array reduce and Array sort
getpaths creates an array in the form
[
["a.b.c", 2],
["d.e", 1],
....
]
not really happy with the code for getpaths - if someone has better code, please let me know
Which can then be sorted easily using Array sort
createPath takes a root object, a path and a value and creates the "path" in the object
so, given {}, "a.b.c", 2
would result in
{
a: {
b: {
c: 2
}
}
}
Since (non numeric) object keys are "sorted" in insertion order [ref], inserting the values in value order produces the object as required
let obj = { a: 50, b: { c: { d: 69, e: 420, f: 21, g: { h: 5, i: 3, }, }, }, j: 1, k: 1000 };
const getpaths = (obj) => {
const paths = [];
const fn = (obj, path = "") => {
Object.entries(obj).forEach(([key, value]) => {
if (typeof value === "object") {
return fn(value, `${path}.${key}`);
}
paths.push([`${path}.${key}`.slice(1), value]);
});
};
fn(obj);
return paths;
};
const createPath = (obj, [path, value]) => {
path.split(".").reduce(
(acc, key, index, array) => acc[key] ||= array[index + 1] ? {} : value, obj);
return obj;
};
const result = getpaths(obj)
.sort(([, a], [, b]) => a - b)
.reduce((acc, item) => createPath(acc, item), {});
console.log(JSON.stringify(result, null, 4));
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
}
]
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; }
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)
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)