Process the Object and store in efficient way - javascript

I receive an Object (Object1) with basic pattern of Key and value, And then I want to process and save output in MongoDB, such that it contain Array objects for "Locations" and "Address" which are dynamic in nature.
Please suggest me any efficient way to do that.
const Object1 = {
date:'07-Sep-2020',
Address_1_Ftime: 2,
Address_1_Rtime: 0,
Address_1_Stime: 49,
Address_2_Ftime: 12,
Address_2_Rtime: 40,
Address_2_Stime: 9
Location1Name:'SAV2',Location1Val:30,
Location2Name:'LT232',Location2Val:130,
}
const Output =
{
date : '07-Sep-2020',
Address:
[
{
name:'Address_1',
Ftime:2,
Rtime:0,
Stime:49
},
{
name:'Address_2',
Ftime:12,
Rtime:40,
Stime:9
}
],
Locations:[
{
text:Location1,
name:SAV2,
value:30
},
{
text:Location1,
name:LT232,
value:130
}
]
}

After you finish your trying, please check the following code.
And let me know if you are not following something there.
const Object1 = {
date:'07-Sep-2020',
Address_1_Ftime: 2,
Address_1_Rtime: 0,
Address_1_Stime: 49,
Address_2_Ftime: 12,
Address_2_Rtime: 40,
Address_2_Stime: 9,
Location1Name:'SAV2',Location1Val:30,
Location2Name:'LT232',Location2Val:130,
};
// Initialize the output
const Output = {
Address: {},
Locations: {}
};
Object.keys(Object1).forEach(key => {
const value = Object1[key];
if (key === 'date') {
Output.date = value;
} else if (key.startsWith('Address')) {
const addressName = key.substr(0, key.length - 6);
const addressFieldKey = key.substr(key.length - 5);
Output.Address[addressName] = Output.Address[addressName] || {};
Output.Address[addressName][addressFieldKey] = value;
} else if (key.startsWith('Location')) {
const fieldKeyLength = key.endsWith('Name') ? 4 : 3;
const locationName = key.substr(0, key.length - fieldKeyLength);
const locationFieldKey = key.substr(key.length - fieldKeyLength);
Output.Locations[locationName] = Output.Locations[locationName] || {};
Output.Locations[locationName][locationFieldKey] = value;
}
});
// At this point, Address and Locations of Output are still objects not array. So you need the following change.
Output.Address = Object.keys(Output.Address).map(key => ({
name: key,
...Output.Address[key]
}));
Output.Locations = Object.keys(Output.Locations).map(key => ({
text: key,
name: Output.Locations[key].Name,
value: Output.Locations[key].Val
}));
console.log(Output);

Related

Generated a Nested Object from TLV

I am trying to parse a TLV string and need to generate a nested object from that string.
const text = 'AA06ClaireCC04JackBB03TomEE05James'
The output needs to look like this:
"Acrobatic Artist": {
"AA": {
"Claire": {
"Curious Camper": {
"CC": {
"Jack": {
"Baddest Banana": {
"BB": {
"Tom": {
"Energetic Elephant": {
"EE": {
"James" : "LASTRECORD"
}
}
}
}
}
}
}
}
}
}
}
Here is what I currently have:
const map = {
AA: 'Acrobatic Artist',
BB: 'Baddest Banana',
CC: 'Curious Camper',
DD: 'Desperate Driver',
EE: 'Energetic Elephant'
}
function createJson(str) {
let json = {}
let remainingText = str
while(remainingText.length > 0) {
const tag = remainingText.substring(0, 2)
const len = remainingText.substring(2, 4)
const val = remainingText.substring(4, len)
const offset = tag.length + len.length + parseInt(len, 16)
remainingText = remainingText.substring(offset)
console.log('new text: ' + remainingText)
json[map[tag]] = {}
json[map[tag]][tag] = {}
json[map[tag]][tag][val] = {}
}
return json
}
But this just creates an object that looks like this:
{
Acrobatic Artist: {
AA: {
Claire: {}
}
},
Baddest Banana: {
BB: {
Tom: {}
}
},
Curious Camper: {
CC: {
Jack: {}
}
},
Energetic Elephant: {
EE: {
James: {}
}
}
}
Here is my fiddle:
https://jsfiddle.net/kzaiwo/y9m2h60t/8/
Note:
Please disregard the LASTRECORD part. I just added that to complete the key-value pair (for the last pair) in the above example. Thank you!
Thanks!
If you keep a reference to a prev value, which starts off as the original json object, you can then continuously update it and its children. When you're updating your object within the while loop you can update prev, and set it to the last child object that you create so that on the next iteration of your loop that particular child object will be updated to contain the new key-value pairs.
const map = {
AA: 'Acrobatic Artist',
BB: 'Baddest Banana',
CC: 'Curious Camper',
DD: 'Desperate Driver',
EE: 'Energetic Elephant'
};
const text = 'AA06ClaireCC04JackBB03TomEE05James';
function createJson(str) {
let json = {};
let prev = json;
let remainingText = str;
while (remainingText.length > 0) {
const tag = remainingText.substring(0, 2);
const len = remainingText.substring(2, 4);
const val = remainingText.substring(4, 4 + parseInt(len, 16));
const offset = tag.length + len.length + parseInt(len, 16);
remainingText = remainingText.substring(offset);
prev[map[tag]] = {};
prev[map[tag]][tag] = {};
prev = prev[map[tag]][tag][val] = {};
}
return json;
}
console.log(createJson(text));
Given the regular structure of your string (2-character code + 2-character number + characters), you can use a simple regex to split out the various parts.
From there you can (flat) map each section into an array of keys.
Finally, you can reduce-right the array to produce the result you want.
const map = {AA:"Acrobatic Artist",BB:"Baddest Banana",CC:"Curious Camper",DD:"Desperate Driver",EE:"Energetic Elephant"};
const text = "AA06ClaireCC04JackBB03TomEE05James";
// Parses a code, length and value from the start of the provided string
const parseSection = (str) => {
const [, code, valueLength] = str.match(/^(\w{2})([a-fA-F0-9]{2})/);
const length = parseInt(valueLength, 16) + 4;
return {
code,
length,
type: map[code],
value: str.slice(4, length),
};
};
// Iterates through the string, extracting sections until finished
const parseTlv = (str) => {
const sections = [];
while (str.length) {
const section = parseSection(str);
sections.push(section);
str = str.slice(section.length);
}
return sections;
};
// Map each section to a flat array of keys then reduce-right to form
// a tree structure
const lastRecord = {};
const result = parseTlv(text)
.flatMap(({ type, code, value }) => [type, code, value])
.reduceRight(
(obj, key) => ({
[key]: obj,
}),
lastRecord
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; }
Here is a two part solution:
for() loop: create an array of items based on <tag><len><val> patterns
.reduce(): build the nested object from the items array
const input = 'AA06ClaireCC04JackBB03TomEE05James';
const tagMap = {
AA: 'Acrobatic Artist',
BB: 'Baddest Banana',
CC: 'Curious Camper',
DD: 'Desperate Driver',
EE: 'Energetic Elephant'
};
let items = [];
for(let i = 0; i < input.length; ) {
let tag = input.substring(i, i + 2);
let len = parseInt(input.substring(i + 2, i + 4), 16);
let val = input.substring(i + 4, i + 4 + len);
items.push([tag, val]);
i += 4 + len;
}
let result = {};
items.reduce((obj, arr) => {
const tag = arr[0];
const val = arr[1];
const name = tagMap[tag] || 'unknown';
//console.log(name, tag, val);
[name, tag, val].forEach(key => {
obj[key] = {};
obj = obj[key];
});
return obj;
}, result);
console.log(result);
Output:
{
"Acrobatic Artist": {
"AA": {
"Claire": {
"Curious Camper": {
"CC": {
"Jack": {
"Baddest Banana": {
"BB": {
"Tom": {
"Energetic Elephant": {
"EE": {
"James": {}
}
}
}
}
}
}
}
}
}
}
}
}
Note: the resulting object has an empty {} as the innermost value; you could replace that with "LASTRECORD" if needed

compare two arrays and remove duplicates [duplicate]

I created an array of objects like so:
[
{
"lat": 12.123,
"lng": 13.213,
"city": "New York"
},
{
"lat": 3.123,
"lng": 2.213,
"city": "New York"
},
{
"lat": 1.513,
"lng": 1.113,
"city": "London"
}
]
I'm trying to create a new array that filters the places to only contains objects that don't have the same city property (lat/lng duplicates are ok). Is there a built in JS or Jquery function to achieve this?
I'd probably use a flags object during the filtering (edit: I wouldn't anymore, see the note at the end of the answer about ES2015's Set), like this:
var flags = {};
var newPlaces = places.filter(function(entry) {
if (flags[entry.city]) {
return false;
}
flags[entry.city] = true;
return true;
});
That uses Array#filter from ECMAScript5 (ES5), which is one of the ES5 additions that can be shimmed (search for "es5 shim" for several options).
You can do it without filter, of course, it's just a bit more verbose:
var flags = {};
var newPlaces = [];
var index;
for (index = 0; index < places.length; ++index) {
if (!flags[entry.city]) {
flags[entry.city] = true;
newPlaces.push(entry);
}
});
Both of the above assume the first object with a given city should be kept, and all other discarded.
Note: As user2736012 points out below, my test if (flags[entry.city]) will be true for cities with names that happen to be the same as properties that exist on Object.prototype such as toString. Very unlikely in this case, but there are four ways to avoid the possibility:
(My usual preferred solution) Create the object without a prototype: var flags = Object.create(null);. This is a feature of ES5. Note that this cannot be shimmed for obsolete browsers like IE8 (the single-argument version of Object.create can be except when that argument's value is null).
Use hasOwnProperty for the test, e.g. if (flags.hasOwnProperty(entry.city))
Put a prefix on that you know doesn't exist for any Object.prototype property, such as xx:
var key = "xx" + entry.city;
if (flags[key]) {
// ...
}
flags[key] = true;
As of ES2015, you could use a Set instead:
const flags = new Set();
const newPlaces = places.filter(entry => {
if (flags.has(entry.city)) {
return false;
}
flags.add(entry.city);
return true;
});
Shortest, but not best performance (see update bellow) solution for es6 :
function unique(array, propertyName) {
return array.filter((e, i) => array.findIndex(a => a[propertyName] === e[propertyName]) === i);
}
performance: https://jsperf.com/compare-unique-array-by-property
You can filter using a Set by only including elements with a property value that has not yet been added to the Set (after which it should be added to the Set). This can be accomplished in one line using the logical and operator (&&). Using this data structure has the advantage of sublinear lookup times (often O(1)).
Below is a general function to obtain a unique array of objects based on a specific property (prop) from an array of objects (arr). Note that in the case of duplicates, only the first object with the property value will be retained.
const getUniqueBy = (arr, prop) => {
const set = new Set;
return arr.filter(o => !set.has(o[prop]) && set.add(o[prop]));
};
Demo:
var places = [{
lat: 12.123,
lng: 13.213,
city: 'New York'
}, {
lat: 3.123,
lng: 2.213,
city: 'New York'
}, {
lat: 3.123,
lng: 4.123,
city: 'Some City'
}];
const getUniqueBy = (arr, prop) => {
const set = new Set;
return arr.filter(o => !set.has(o[prop]) && set.add(o[prop]));
};
console.log(getUniqueBy(places, 'city'));
https://lodash.com/docs#uniqBy
https://github.com/lodash/lodash/blob/4.13.1/lodash.js#L7711
/**
* This method is like `_.uniq` except that it accepts `iteratee` which is
* invoked for each element in `array` to generate the criterion by which
* uniqueness is computed. The iteratee is invoked with one argument: (value).
*
* #static
* #memberOf _
* #since 4.0.0
* #category Array
* #param {Array} array The array to inspect.
* #param {Array|Function|Object|string} [iteratee=_.identity]
* The iteratee invoked per element.
* #returns {Array} Returns the new duplicate free array.
* #example
*
* _.uniqBy([2.1, 1.2, 2.3], Math.floor);
* // => [2.1, 1.2]
*
* // The `_.property` iteratee shorthand.
* _.uniqBy([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x');
* // => [{ 'x': 1 }, { 'x': 2 }]
*/
I expanded a bit on #IgorL solution, but extended prototype and gave it a selector function instead of a property to make it a little more flexible:
Array.prototype.unique = function(selector) {
return this.filter((e, i) => this.findIndex((a) => {
if (selector) {
return selector(a) === selector(e);
}
return a === e;
}) === i);
};
Usage:
// with no param it uses strict equals (===) against the object
let primArr = ['one','one','two','three','one']
primArr.unique() // ['one','two','three']
let a = {foo:123}
let b = {foo:123}
let fooArr = [a,a,b]
fooArr.unique() //[a,b]
// alternatively, you can pass a selector function
fooArr.unique(item=>item.foo) //[{foo:123}] (first "unique" item returned)
Definitely NOT the most performant way to do this but as long as the selector is simple and the array isn't massive, it should work fine.
In Typescript
Array.prototype.unique = function<T>(this: T[], selector?: (item: T) => object): T[] {
return this.filter((e, i) => this.findIndex((a) => {
if (selector) {
return selector(a) === selector(e);
}
return a === e;
}) === i);
};
My suggestion :
Array.prototype.uniqueCity = function() {
var processed = [];
for (var i=this.length-1; i>=0; i--){
if (processed.indexOf(this[i].city)<0) {
processed.push(this[i].city);
} else {
this.splice(i, 1);
}
}
}
in use :
places.uniqueCity();
or
Array.prototype.uniqueObjectArray = function(field) {
var processed = [];
for (var i=this.length-1; i>=0; i--) {
if (this[i].hasOwnProperty(field)) {
if (processed.indexOf(this[i][field])<0) {
processed.push(this[i][field]);
} else {
this.splice(i, 1);
}
}
}
}
places.uniqueObjectArray('city');
With the above you can sort the array by any of the fields in the objects, even if they are not present for some of the objects.
or
function uniqueCity(array) {
var processed = [];
for (var i=array.length-1; i>=0; i--){
if (processed.indexOf(array[i].city)<0) {
processed.push(array[i].city);
} else {
array.splice(i, 1);
}
}
return array;
}
places = uniqueCity(places);
You could use a Map so the entries with the same key property (in your case 'city') only appear once
module.exports = (array, prop) => {
const keyValueArray = array.map(entry => [entry[prop], entry]);
const map = new Map(keyValueArray);
return Array.from(map.values());
};
More info about Map and array objects here
Basic example on Codepen
Another option:
const uniqueBy = prop => list => {
const uniques = {}
return list.reduce(
(result, item) => {
if (uniques[item[prop]]) return result
uniques[item[prop]] = item
return [...result, item]
},
[],
)
}
const uniqueById = uniqueBy('id')
uniqueById([
{ id: 1, name: 'one' },
{ id: 2, name: 'two' },
{ id: 1, name: 'one' },
{ id: 3, name: 'three' }
])
You can paste it on your console to see it working.
It should work for the scenario presented and a few others.
We can create the list of unique objects by any property using JavaScript Map.
For example :
var places = [{ 'lat': 12.123, 'lng': 13.213, 'city': "New York"},
{ 'lat': 3.123, 'lng': 2.213, 'city': "New York"},
{ 'lat': 43.123, 'lng': 12.213, 'city': "London"}];
var cityMap = new Map();
places.forEach(p=> cityMap.set(p.city, p));
console.log([...cityMap.values()]);
Execute code snippet to see the result.
As pointed out in the comments, you could use an object as a map, which will allow you to avoid duplicates, you can then enumerate the properties of the object.
working fiddle: http://jsfiddle.net/gPRPQ/1/
var places = [];
var a = {};
a.lat = 12.123;
a.lng = 13.213;
a.city = "New York";
places.push(a);
var b = {};
b.lat = 3.123;
b.lng = 2.213;
b.city = "New York";
places.push(b);
var unique = {}
for (var i = 0; i < places.length; i++) {
var place = places[i];
unique[place.city] = place;
}
for (var name in unique) {
var place = unique[name];
console.log(place);
}
var places = [];
var a = {};
a.lat = 12.123;
a.lng = 13.213;
a.city = "New York";
places.push(a);
var b = {};
b.lat = 3.123;
b.lng = 2.213;
b.city = "New York";
places.push(b);
getUniqAR(places,'city'); //Return Uniq Array by property
function getUniqAR(Data,filter){
var uniar =[];
Data.forEach(function(item,ind,arr){
var dupi=false;
if(!uniar.length) uniar.push(item) //push first obj into uniq array
uniar.forEach(function(item2, ind2,arr){
if(item2[filter] == item[filter]){ //check each obj prop of uniq array
dupi=true; //if values are same put duplicate is true
}
})
if(!dupi){ uniar.push(item)} //if no duplicate insert to uniq
})
console.log(uniar)
return uniar;
}
In simple Javascript code to remove duplicate cities from places array list is
var places = [{ 'lat': 12.123, 'lng': 13.213, 'city': "New York"},
{ 'lat': 3.123, 'lng': 2.213, 'city': "New York"},
{ 'lat': 43.123, 'lng': 12.213, 'city': "London"}];
var unique = [];
var tempArr = [];
places.forEach((value, index) => {
if (unique.indexOf(value.city) === -1) {
unique.push(value.city);
} else {
tempArr.push(index);
}
});
tempArr.reverse();
tempArr.forEach(ele => {
places.splice(ele, 1);
});
console.log(places);
Generic Typescript answer based on https://stackoverflow.com/a/18773857/49564 above:
export function isDistinct<T>(mapper: (value: T) => string): (value: T) => boolean {
const keys: { [index: string]: boolean } = {};
return (entry: T) => {
const key = mapper(entry);
if (keys[key] !== undefined) {
return false;
}
return keys[key] = true;
};
}
// Usage example:
const items = [ { id: 1 }, { id: 2 }, { id: 3 }, { id: 1 } ];
const unique = items.filter(isDistinct(i => i.id));
I think you want this,
NOTE: No library is required.
let array = [{ id: 1}, {id: 2}, {id: 3}];
function addUniqeObj(data) {
let index = -1;
for(let i = 0, i < array.length; i++) {
if(array[i].id === data.id) {
index = i;
}
}
if(index > -1) {
array[index] = data;
} else {
array.push(data)
}
}
Another variation of the rafaelbiten approach:
const dedupExample = [
{id: 1, c: 'whatever'},
{id: 1, c: '1whatever'},
{id: 2, c: '2whatever'},
{id: 2, c: '2whatever'},
{id: 3, c: '2whatever'},
]
const getUniqueBy = (prop, list) => {
const objUniq = list.reduce((res, item) => ({ ...res, [item[prop]]: item }), {})
return Object.keys(objUniq).map(item => objUniq[item])
}
const uniq = getUniqueBy('id', dedupExample)
console.info('info', { uniq })
/* [
{id: 1, c: 'whatever'},
{id: 2, c: '2whatever'},
{id: 3, c: '2whatever'},
] */
const distinctArrayByCity= [
...new Map(array.map((item) => [item.city, item])).values(),
];
This thread may be old but thought I should share it. It is based on Pure JavaScript and removes Duplicate Objects based on the Properties Specified.
function removeDuplicates(originalArray, properties) {
var newArray = [];
var index = 0;
var lookupObject = {};
var totalProperties = properties.length;
for (var i = 0; i < originalArray.length; i++) {
var exists = false;
for (var a = 0; a < newArray.length; a++) {
var propsFound = 0;
for (var b = 0; b < totalProperties; b++) {
if (originalArray[i][properties[b]] == newArray[a][properties[b]]) {
propsFound++;
}
}
//If there is a match then break the for loop
if (propsFound == totalProperties) {
exists = true;
break;
}
} //End of New Array
if (!exists) {
newArray[index] = originalArray[i];
index++;
}
} //End of originalArray
return newArray;
}
You can view the fiddle here

how to assign a key in object using javascript

I want to total all same ID and assign a specific key
var ingredArray= [{"inventory_id":1,"pergram":222},{"inventory_id":1,"pergram":33},{"inventory_id":2,"pergram":424},{"inventory_id":2,"pergram":22},{"inventory_id":3,"pergram":400},{"inventory_id":5,"pergram":200}]
let deduction={};
ingredArray.forEach(function (item) {
if (deduction.hasOwnProperty(item.inventory_id)) {
deduction[item.inventory_id] = deduction[item.inventory_id] + parseFloat(item.pergram);
} else {
deduction[item.inventory_id] = parseFloat(item.pergram);
}
});
console.log(deduction);
this is the result of my code
{1: 255, 2: 446, 3: 400, 5: 200}
I want to achieve
{"inventory_id":1,"pergram":255},{"inventory_id":2,"pergram":446},{"inventory_id":3,"pergram":400},{"inventory_id":5,"pergram":200}
Try this
var ingredArray = [{ "inventory_id": 1, "pergram": 222 }, { "inventory_id": 1, "pergram": 33 }, { "inventory_id": 2, "pergram": 424 }, { "inventory_id": 2, "pergram": 22 }, { "inventory_id": 3, "pergram": 400 }, { "inventory_id": 5, "pergram": 200 }]
var helper = {};
let deduction = ingredArray.reduce(function (r, o) {
var key = o.inventory_id;
if (!helper[key]) {
helper[key] = Object.assign({}, o); // create a copy of o
r.push(helper[key]);
} else {
helper[key].pergram += o.pergram;
}
return r;
}, []);
console.log(deduction);
reduce over the array of objects building a new object of summed values based on key, and then grab the Object.values.
const data = [{"inventory_id":1,"pergram":222},{"inventory_id":1,"pergram":33},{"inventory_id":2,"pergram":424},{"inventory_id":2,"pergram":22},{"inventory_id":3,"pergram":400},{"inventory_id":5,"pergram":200}];
const out = data.reduce((acc, c) => {
// Grab the id and pergram variables from the
// new object in the iteration
const { inventory_id: id, pergram } = c;
// If the the accumulator object doesn't have a key that
// matches the id create a new new object, and set the
// pergram variable to zero
acc[id] = acc[id] || { inventory_id: id, pergram: 0 };
// And then add the pergram value to the
// pergram object property
acc[id].pergram += pergram;
// Return the accumulator for the next iteration
return acc;
}, {});
console.log(Object.values(out));
Use Map to store the object reference based on unique inventory_id and call Array.reduce() method over list of objects.
var ingredArray= [{"inventory_id":1,"pergram":222},{"inventory_id":1,"pergram":33},{"inventory_id":2,"pergram":424},{"inventory_id":2,"pergram":22},{"inventory_id":3,"pergram":400},{"inventory_id":5,"pergram":200}];
const processArray = (list) => {
const map = new Map();
return list.reduce((accumulator, obj) => {
const inventory = map.get(obj.inventory_id);
if (inventory) {
inventory.pergram += obj.pergram;
} else {
accumulator.push(obj);
map.set(obj.inventory_id, obj);
}
return accumulator;
}, []);
}
console.log(processArray(ingredArray));

How do I set the value of a key in on array as the key in an other array?

I am trying to get the value feature_1 of a key name from the array data and set feature_1 as the key of another array asset which has an array as value.
Example :
//input:
data: {
name: "feature_1",
value_a: 1,
value_b: 2
}
//Output:
asset: {
feature_1:[1,2]
}
You can try:
var asset = {};
if ('name' in data) {
var tmp = [];
for (k in data){
if (k != 'name') tmp.push(data[k]);
}
asset[data['name']] = tmp;
}
and in case your interpreter supports ES6 you can use, for example:
let {name, ...v} = data;
let asset = {[name]: Object.values(v)};
If supports same keys in a JSON object ( It seems not ) You can do it like this:
let data= {
name: "feature_1",
value_a: 1,
value_b: 2,
value_x: 500,
name: "feature_2",
value_a: 17,
value_b: 21,
value_x: 510
}
console.log(
Object.entries(data).reduce((a,[key,value],index)=>{
if (key==='name')
return {index, assets: {...a.assets, [value]:[]}};
return {
index : a.index,
assets : {
...a.assets,
[Object.entries(a.assets)[a.index][0]] : [...Object.entries(a.assets)[a.index][1],value]
}};
},{index:0,assets:{}}).assets
);
But we know the correct way is using separated array rows.
This can be accomplished the following way:
const obj = {
data: {
name: "feature_1",
value_a: 1,
value_b: 2
}
};
const output = {
assets: {
[obj.data.name]: Object.values(obj.data).filter(el => el !== obj.data.name)
}
}
console.log(output);

The proper ways to stack optioned promises

What would be the proper or the best way to collect all data from DB with promises, but with using native Node promises.
The goal is only to present what is selected:
const allPromises = [];
const selected = {
sectionA: true,
sectionB: false,
sectionCIds: [ 1, 2, 4 ],
};
if (selected.sectionA) {
allPromises.push(getSectionADataFromDbPromise());
}
if (selected.sectionB) {
allPromises.push(getSectionBDataFromDbPromise());
}
if (selected.sectionCIds.length > 0) {
allPromises.push(selected.sectionCIds
.map(getSectionCDataFromDbPromise)
);
}
Promise.all(allPromises)
.then((allResults) => {
if (selected.sectionA) {
dataA = allResults[0];
}
if (selected.sectionA) {
dataB = allResults[1];
}
if (selected.sectionC) {
dataC = allResults[2]; // <-- this will not work if B is not selected
}
// ... same logic to build report: return Promise.all()...
});
Possible solutions:
Track index for each data selected (eg. index of C will be 1)
Object Map
Add else { allPromises.push(Promise.resolve(null)) } to every if
Is there maybe an easier or one of this will be the proper way?
Don't use push on the arrays conditionally, but always put the same value at the same index. Even if the value is nothing - Promise.all will handle that just fine.
const selected = {
sectionA: true,
sectionB: false,
sectionCIds: [ 1, 2, 4 ],
};
Promise.all([
selected.sectionA ? getSectionADataFromDbPromise() : null,
selected.sectionB ? getSectionBDataFromDbPromise() : null,
Promise.all(selected.sectionCIds.map(getSectionCDataFromDbPromise))
]).then(([dataA, dataB, dataC]) => {
if (selected.sectionA) {
// use dataA
}
if (selected.sectionA) {
// use dataB
}
if (dataC.length) { // same as selected.selectionCIds.length
// use dataC
}
});
What do you think about this ? It's bigger, it's heavier, it's more difficult, but it's all automatized and fully evolutive. Wanna handle a new parameter ? A parameter have data now ? Change the map only.
I create a map that would contains everything we need to use a loop. The state of the data (activated or not), the function to call to get the data and so on.
const mapSelected = {
sectionA: {
state: true,
func: getSectionADataFromDbPromise,
},
sectionB: {
state: false,
func: getSectionBDataFromDbPromise,
},
sectionC: {
state: true,
func: getSectionCDataFromDbPromise,
data: [
1,
2,
4,
],
},
};
Then we create the promise array using the map we has created. Handling the case with data and without data.
const promises = Object.values(mapSelected).reduce((tmp, {
state,
func,
data,
}) => {
if (!state) return tmp;
if (data && data.length) {
return [
...tmp,
...data.map(x => func.call(this, x)),
];
}
return [
...tmp,
func.call(this),
];
});
Then we create arrays from the promise return for each key on the map. You can change how I present the data, I didn't knew what you really wanted there.
Promise.all(promises)
.then((allResults) => {
let i = 0;
const [
dataA,
dataB,
dataC,
] = Object.values(mapSelected).reduce((tmp, {
state,
data,
}, xi) => {
if (!state) return tmp;
if (data && data.length) {
data.forEach(x => (tmp[xi].push(allPromises[i++])));
return tmp;
}
tmp[xi].push(allPromises[i++]);
return tmp;
}, Object.values(mapSelected).map(() => []));
});
#EDIT
I just did a snippet about the code I've made, run it
function a() {
return 1;
}
const mapSelected = {
sectionA: {
state: true,
func: a,
},
sectionB: {
state: false,
func: a,
},
sectionC: {
state: true,
func: a,
data: [
1,
2,
4,
],
},
};
const allPromises = [
0,
1,
2,
3,
4,
];
let i = 0;
const [
dataA,
dataB,
dataC,
] = Object.values(mapSelected).reduce((tmp, {
state,
data,
}, xi) => {
if (!state) return tmp;
if (data && data.length) {
data.forEach(x => (tmp[xi].push(allPromises[i++])));
return tmp;
}
tmp[xi].push(allPromises[i++]);
return tmp;
}, Object.values(mapSelected).map(() => []));
console.log(dataA);
console.log(dataB);
console.log(dataC);
Unfortunately, unlike libraries such as Q, the standard Promise does not expose a variant of all taking an object of promises.
However, we can use the new ES2015 and ES2017 Object utility methods to assist us in keeping the code readable.
const allPromises = {};
const selected = {
sectionA: true,
sectionB: false,
sectionCIds: [1, 2, 4],
};
if (selected.sectionA) {
allPromises.sectionA = getSectionADataFromDbPromise();
}
if (selected.sectionB) {
allPromises.sectionB = getSectionBDataFromDbPromise();
}
if (selected.sectionBIds.length > 0) {
allPromises.sectionC = Promise.all(selected.sectionBIds
.map(getSectionCDataFromDbPromise)
);
}
Now we can write
Promise.all(Object.entries(allPromises).map(([key, promise]) =>
promise.then(value => ({[key]: value}))
))
.then(allResults => {
const results = Object.assign({}, ...allResults);
const data = {
a: results.sectionA,
b: results.sectionB,
c: results.sectionB && results.sectionC
};
// ... same logic to build report: return Promise.all()...
});

Categories

Resources