Related
I have a JSON array
{
"data": [
{
"id": 659,
"source_id": 1,
"created_at": "2023-01-13T06:35:08.000000Z",
"products": [
{
"name": "532",
"properties": [
{
"name": "color",
"value": "blue"
},
{
"name": "size",
"value": "1"
}
],
}
]
},
{
"id": 658,
"source_id": 2,
"created_at": "2023-01-12T21:36:06.000000Z",
"products": [
{
"name": "532",
"properties": [
{
"name": "color",
"value": "khaki"
},
{
"name": "size",
"value": "2"
}
],
}
]
},
},
],
}
All code I have so far:
var rows = [], sortOrder = ['fabric', 'color', 'size'], orderSource = [{"Insta" : 1, "Retail" : 2}];
dataSet.forEach((e) => {
e.products.forEach((product) => {
product.properties = product.properties.sort((a, b) => {
return sortOrder.indexOf(a.name) - sortOrder.indexOf(b.name);
});
product.properties = sortOrder.map((i => name => product.properties[i].name === name ?
product.properties[i++] : {name, value : ''})(0));
rows.push([e.id, e.source_id, new Date(e.created_at).toLocaleDateString('uk-UK'),
product.name].concat(product.properties.map(p => p.value)).concat([product.quantity,
product.comment]));
console.log(rows);
});
});
Console output looks like this
[ 659, 1, '13.01.2023', '532', 'blue', '1' ],
[ 658, 2, '12.01.2023', '532', 'khaki', '2' ]
I need the data from the array orderSource = [{"Insta" : 1, "Retail" : 2}] to be reassigned to the resulting array so that it looks like this
[ 659, 'Insta', '13.01.2023', '532', 'blue', '1' ],
[ 658, 'Retail', '12.01.2023', '532', 'khaki', '2' ]
This is necessary in order to then write the array to the Google spreadsheet
I am new to programming, so I do not consider it necessary to list here all my attempts to reassign values in the resulting array))
Any of your help is welcome
Use the array method Array.find() on the object's keys to find the relevant key (e.g. Insta, Retail).
const rows = [],
sortOrder = ['fabric', 'color', 'size'],
orderSource = [{
"Insta": 1,
"Retail": 2
}];
dataSet.forEach((e) => {
e.products.forEach((product) => {
product.properties = product.properties.sort((a, b) => {
return sortOrder.indexOf(a.name) - sortOrder.indexOf(b.name);
});
product.properties = sortOrder.map((i => name => product.properties[i].name === name ?
product.properties[i++] : {
name,
value: ''
})(0));
// Use the Array.find() method on the object's keys
const key = Object.keys(orderSource[0]).find(key => orderSource[0][key] === e.source_id);
rows.push([e.id, key, new Date(e.created_at).toLocaleDateString('uk-UK'),
product.name
].concat(product.properties.map(p => p.value)).concat([product.quantity,
product.comment
]));
});
});
console.log(rows);
kind of struggling around with a mongodb query. My mongo database has following structure:
name: String
test: String
competences: [{name: String, code: String, value: Float}]
subcompetences: [{name: String, code: String, value: Float}]
My query looks like this:
async function getAggregatedDataForCompetences(filter, category) {
return await getCollection('competences').aggregate([
{$match: { $or: filter }},
{$unwind: "$competences" },
{$group: {
_id: "$_id",
code: { $first: "$competences.code" },
name: { $first: "$competences.name"},
avgValue: { $avg: "$competences.value" },
subcompetences: { $first: "$subcompetences"},
}
},
{$unwind: "$subcompetences" },
{$group: {
_id: "$subcompetences.code",
code: { $first: "$subcompetences.code" },
name: { $first: "$subcompetences.name"},
avgValue: { $avg: "$subcompetences.value" },
}}
]).toArray();
}
What am I trying to do is unwind the first (competences) array for all elements, group them by and calculate the average value for each item. The same procedure is repeated for the following subcompetences array of objects. As a result I'm getting only the average values for the last subcompetences array. Do you have any idea how can I reach the following result:
{
competences: [{name: String, code: String, avgValue: Float}],
subcompetences: [{name: String, code: String, avgValue: Float}]
}
$facet to the rescue -- the "multigroup" operator. Given input like this:
var r =
[
{
"_id" : 0,
"name": "N1",
"competences": [
{name: "AAA", code: "A", value: 1.1},
{name: "BBB", code: "B", value: 2.2},
{name: "CCC", code: "C", value: 3.3}
],
"subcompetences": [
{name: "DDD", code: "D", value: 4.4},
{name: "EEE", code: "E", value: 5.5},
{name: "FFF", code: "F", value: 6.6}
]
}
,{
"_id" : 1, "name": "N2",
"competences": [
{name: "AAA", code: "A", value: 9.9},
{name: "BBB", code: "B", value: 8.8},
{name: "KKK", code: "K", value: 11.11}
],
"subcompetences": [
{name: "FFF", code: "F", value: 4.9},
{name: "GGG", code: "G", value: 6.7}
]
}
];
then $facet will allow you to do two groups "in parallel". Indeed, you can do two or more complete pipelines at the same time (some restrictions apply):
db.foo.aggregate([
{$facet: {
"avg_competences": [
{$unwind: "$competences"}
,{$group: {_id: "$competences.code",
name: {$first: "$competences.name"},
count: {$sum: 1},
avgval: {$avg: "$competences.value"},
}}
]
,"avg_subcompetences": [
{$unwind: "$subcompetences"}
,{$group: {_id: "$subcompetences.code",
name: {$first: "$subcompetences.name"},
count: {$sum: 1},
avgval: {$avg: "$subcompetences.value"},
}}
]
}
}
// The output of the stage above will be a *single* doc with two fields,
// avg_competence and avg_subcompetences. Let's add more fields to this doc!
,{$addFields: {N: {$reduce: {
input: {$concatArrays: ["$avg_competences","$avg_subcompetences"]},
initialValue: 0,
in:{$sum: [ "$$value", "$$this.count"]}
}}
}}
]);
to yield:
{
"avg_competences" : [
{
"_id" : "K",
"name" : "KKK",
"count" : 1,
"avgval" : 11.11
},
{
"_id" : "C",
"name" : "CCC",
"count" : 1,
"avgval" : 3.3
},
{
"_id" : "B",
"name" : "BBB",
"count" : 2,
"avgval" : 5.5
},
{
"_id" : "A",
"name" : "AAA",
"count" : 2,
"avgval" : 5.5
}
],
"avg_subcompetences" : [
{
"_id" : "G",
"name" : "GGG",
"count" : 1,
"avgval" : 6.7
},
{
"_id" : "F",
"name" : "FFF",
"count" : 2,
"avgval" : 5.75
},
{
"_id" : "E",
"name" : "EEE",
"count" : 1,
"avgval" : 5.5
},
{
"_id" : "D",
"name" : "DDD",
"count" : 1,
"avgval" : 4.4
}
],
"N" : 11
}
I am sure there is a clean way to do this, but I have no idea how to do it. I want to pluck a column out such that I am only returning the first occurrence of a value, but I want to keep the key that went with it.
I have a dataset that I want reduced. I want to pluck out the 'precip'.
Say I have this:
[
"2019-01-01" => {"temp" : "cold", "season" : "winter", "precip" : "snow"},
"2019-02-01" => {"temp" : "cold", "season" : "winter", "precip" : "none"},
"2019-03-01" => {"temp" : "mild", "season" : "spring", "precip" : "rain"},
"2019-04-01" => {"temp" : "mild", "season" : "spring", "precip" : "none"},
"2019-05-01" => {"temp" : "warm", "season" : "spring", "precip" : "rain"},
"2019-06-01" => {"temp" : "warm", "season" : "summer", "precip" : "hail"},
"2019-07-01" => {"temp" : "hot", "season" : "summer", "precip" : "none"}
]
I would like to end up with this:
[
"2019-01-01" => "snow",
"2019-02-01" => "none",
"2019-03-01" => "rain",
"2019-06-01" => "hail"
]
I would think that Array.map has something to do with this, but I don't know how to return the key/value pair instead of just a value (i.e. map(function(d) { return d.precip }) )
What is the smooth way to do this?
Thanks in advance.
You could create a Map and take only the first item with the same key.
function getFirstPrecip(data) {
return Object.assign({}, ...Array.from(
data.reduce((m, o) => {
var [[k, { precip }]] = Object.entries(o);
return m.has(precip) ? m : m.set(precip, k);
}, new Map),
([k, v]) => ({ [v]: k })
));
}
var data = [{ "2019-01-01": { temp: "cold", season: "winter", precip: "snow" } }, { "2019-02-01": { temp: "cold", season: "winter", precip: "none" } }, { "2019-03-01": { temp: "mild", season: "spring", precip: "rain" } }, { "2019-04-01": { temp: "mild", season: "spring", precip: "none" } }, { "2019-05-01": { temp: "warm", season: "spring", precip: "rain" } }, { "2019-06-01": { temp: "warm", season: "summer", precip: "hail" } }, { "2019-07-01": { temp: "hot", season: "summer", precip: "none" } }];
console.log(getFirstPrecip(data));
You can iterate the keys for the main object, and create a new object that will use those keys and assign the precip attribute value as value for the new key=>value pair:
var dates = {
"2019-01-01" : {"temp" : "cold", "season" : "winter", "precip" : "snow"},
"2019-02-01" : {"temp" : "cold", "season" : "winter", "precip" : "none"},
"2019-03-01" : {"temp" : "mild", "season" : "spring", "precip" : "rain"},
"2019-04-01" : {"temp" : "mild", "season" : "spring", "precip" : "none"},
"2019-05-01" : {"temp" : "warm", "season" : "spring", "precip" : "rain"},
"2019-06-01" : {"temp" : "warm", "season" : "summer", "precip" : "hail"},
"2019-07-01" : {"temp" : "hot", "season" : "summer", "precip" : "none"}
};
var result = Object.keys(dates).reduce((k,d) => {return [...k, {[d]:dates[d].precip}]}, [])
console.log(result)
I have an array of object, which has be combined based on a unique attribute.
For example:
[
{ "macId": "123", "input": 30},
{ "macId": "567", "input": 40},
{ "macId": "123", "power": 100},
{ "macId": "567", "power": 250}
]
I want the resultant array of object as the following:
[
{ "macId": "123", "input": 30, "power": 100},
{ "macId": "567", "input": 40 "power": 250}
]
Is this possible with lodash? If not, how can I write a short code for this to do so?
Using lodash I would try groupBy using macId, then using merge to "combine" the objects in the resulting array.
const after =
_(before)
.groupBy('macId')
.map(group => _.merge(...group))
.value();
I would suggest using an ES6 Map in combination with Object.assign:
const arr = [
{ "macId": "123", "input": 30},
{ "macId": "567", "input": 40},
{ "macId": "123", "power": 100},
{ "macId": "567", "power": 250}
]
const result = [...arr.reduce((acc, obj) =>
acc.set(obj.macId, Object.assign(acc.get(obj.macId) || {}, obj)), new Map).values()];
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can do the following, with Object#assign and Array#find :
var myMergedArray = arr.reduce((acc = [], val) => {
if (acc.filter(obj => obj.macId === val.macId).length !== 0) {
Object.assign(acc.find((el) => el.macId === val.macId), val)
} else {
acc.push(val);
}
return acc;
}, [])
Snippet :
var arr = [
{ "macId": "123", "input": 30},
{ "macId": "567", "input": 40},
{ "macId": "123", "power": 100},
{ "macId": "567", "power": 250}
]
var myMergedArray = arr.reduce((acc = [], val) => {
if (acc.filter(obj => obj.macId === val.macId).length !== 0) {
Object.assign(acc.find((el) => el.macId === val.macId), val)
} else {
acc.push(val);
}
return acc;
}, [])
console.log(myMergedArray);
Since you have tagged lodash you can easily group it and map it back (merge using reduce or _.merge) easily.
var arr = [
{ "macId": "123", "input": 30},
{ "macId": "567", "input": 40},
{ "macId": "123", "power": 100},
{ "macId": "567", "power": 250}
]
var unique = _.chain(arr).groupBy('macId').map(v => _.merge(...v)).value();
console.log(unique)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>
I have this data set in object, keys are strings
{ '17': '17-17:30',
'20': '20:00-21',
'21': '21-22',
'22': '22-23',
'23': '23-24',
'01': '1-2',
'02': '2-3',
'03': '3-4',
'04': '4-5',
'05': '5-6',
'06': '6-7',
'07': '7-7:30',
'08': '08:50-9' }
I want to arrange them numerically so that 01 comes first and 23 comes last.
Here's the code I'm using:
var sort_object = function(map) {
var keys = _.sortBy(_.keys(map), function(a) { return Number(a); });
var newmap = {};
_.each(keys, function(k) {
newmap[k] = map[k];
});
return newmap;
}
it still returns 17 in the beginning.
Where did I go wrong?
As Blender pointed out, objects are not ordered. You need to use an array. There are several ways you could set up this array, and once you have the data in an array it's very easy to sort it.
Here are a couple of examples. First, let's try making this an array of arrays. The outer array is the list of elements, and each element of this array is itself an array of two elements, your key and your value.
So paste this code into your favorite JavaScript console, such as the one in Chrome:
var data = [
[ '17', '17-17:30' ],
[ '20', '20:00-21' ],
[ '21', '21-22' ],
[ '22', '22-23' ],
[ '23', '23-24' ],
[ '01', '1-2' ],
[ '02', '2-3' ],
[ '03', '3-4' ],
[ '04', '4-5' ],
[ '05', '5-6' ],
[ '06', '6-7' ],
[ '07', '7-7:30' ],
[ '08', '08:50-9' ]
];
var result = data.slice().sort( function( a, b ) {
return +a[0] - +b[0];
});
JSON.stringify( result, null, 4 );
It will log:
[
[
"01",
"1-2"
],
[
"02",
"2-3"
],
[
"03",
"3-4"
],
[
"04",
"4-5"
],
[
"05",
"5-6"
],
[
"06",
"6-7"
],
[
"07",
"7-7:30"
],
[
"08",
"08:50-9"
],
[
"17",
"17-17:30"
],
[
"20",
"20:00-21"
],
[
"21",
"21-22"
],
[
"22",
"22-23"
],
[
"23",
"23-24"
]
Or, instead of an array of arrays, you can use an array of objects. This is often more convenient to work with. Paste this code into the JavaScript console:
var data = [
{ key: '17', value: '17-17:30' },
{ key: '20', value: '20:00-21' },
{ key: '21', value: '21-22' },
{ key: '22', value: '22-23' },
{ key: '23', value: '23-24' },
{ key: '01', value: '1-2' },
{ key: '02', value: '2-3' },
{ key: '03', value: '3-4' },
{ key: '04', value: '4-5' },
{ key: '05', value: '5-6' },
{ key: '06', value: '6-7' },
{ key: '07', value: '7-7:30' },
{ key: '08', value: '08:50-9'}
];
var result = data.slice().sort( function( a, b ) {
return +a.key - +b.key;
});
JSON.stringify( result, null, 4 );
It will log:
[
{
"key": "01",
"value": "1-2"
},
{
"key": "02",
"value": "2-3"
},
{
"key": "03",
"value": "3-4"
},
{
"key": "04",
"value": "4-5"
},
{
"key": "05",
"value": "5-6"
},
{
"key": "06",
"value": "6-7"
},
{
"key": "07",
"value": "7-7:30"
},
{
"key": "08",
"value": "08:50-9"
},
{
"key": "17",
"value": "17-17:30"
},
{
"key": "20",
"value": "20:00-21"
},
{
"key": "21",
"value": "21-22"
},
{
"key": "22",
"value": "22-23"
},
{
"key": "23",
"value": "23-24"
}
]
Either way, as you can see, once you have your data in a suitable format (i.e. the data as a whole needs to be an array), then it becomes very simple to sort it.
Let's look at that sort function in more detail (using the second example):
var result = data.slice().sort( function( a, b ) {
return +a.key - +b.key;
});
In this code a.key gets the value for the key property of one of the array elements. The + in front of that converts the string to a number - much like using the Number function but a simpler way to do it. Similarly for +b.key. And then subtracting those two numbers gives the correct sort function return: a positive, negative, or zero value depending on whether a.key or b.key is the greater of the two or if they're equal.
The first example works similarly, just using +a[0] - +b[0] instead of +a.key - +b.key.
The .slice() call in each example makes a copy of the array. You could omit that if you want to sort the original array.
The only way to imply order would be to use an array. How about an array of key/value objects, eg
return Object.keys(map).sort().map(function(k) {
return {key: k, value: foo[k]}
});