I have a collection of objects that I want to retrieve. The objects have some date key:value pairs in them and I want to return all of those as a proper Javascript date. I don't want to declare them all one-by-one, as there are some dates that only exist on some objects, some I might not know about and in general, it's frustrating to declare everything one-by-one.
Here is my code that does not work, how could I get it working?
async function getChargesFromDatabase() {
const chargesCol = fsExpenses.collection('charges');
const chargesDocs = (await chargesCol.limit(50).orderBy('startTs', 'desc').get()).docs.map((doc) => {
const returnDoc: any = {};
for (const [key, value] of Object.entries(Object.entries(doc.data()))) {
returnDoc[key] = value?.toDate() ?? value;
}
return returnDoc;
});
return chargesDocs;
}
You will have to check all the keys as you are doing now by checking if a field is instance of Firestore Timestamp. Try using the following function:
const convertTimestamps = (obj: any) => {
if (obj instanceof firebase.firestore.Timestamp) {
return obj.toDate();
} else if (obj instanceof Object) {
// Check for arrays if needed
Object.keys(obj).forEach((key) => {
obj[key] = convertTimestamps(obj[key]);
});
}
return obj;
};
async function getChargesFromDatabase() {
const chargesCol = fsExpenses.collection('charges');
const chargesSnap = await chargesCol.limit(50).orderBy('startTs', 'desc').get()
const chargesDocs = chargesSnap.docs.map((doc) => convertTimestamps(doc.data()))
}
How, sorry for this weird title, i didn't know how to put this... Here my explanation:
I have a function where I define variable (I'm looping over an array and if condition is matched, I define let). It works everytime item.dataset.category changes, so after every scroll.
After the loop, I call another function, where I use this variable as an argument. In the second function I use it to check if another condition is matched:
//first function
const getDataset = (e) => {
let dataset;
const cat = Array.from(categories.children);
cat.forEach((item) => {
if (item.className.includes('active')) {
dataset = item.dataset.category;
}
});
changeNavActive(dataset);
};
//second function
const changeNavActive = (dataset) => {
const navItems = Array.from(navList.children);
navItems.forEach((item) => {
item.classList.remove('active');
if (item.dataset.category === dataset) {
item.classList.add('active');
}
});
};
It's not working and I think I understand why - callign of second function is at the same time as declaring variable, so I geting this let in the next call. The result is that second function works with delay of one scroll.
This is a function which calls getDataset():
const scrollRows = (e) => {
if (window.scrollY > slider.clientHeight) {
e.deltaY > 0 ? move++ : move--;
getDataset();
if (move > categories.children.length - 1)
move = categories.children.length - 1;
}
}
How to fix this?
The code below works for me
Promise.all([first, second, third]).then([first, second, third] => {
console.log(second);
});
I know that console.log(second) will give me the value with the key second.
My promises are dynamically set and now it looks like below:
let collection = [second, third];
Promise.all(collection).then((collection) => {
console.log(collection);
});
In this example I set two values in collection. In real life it can include more or less values.
When I use console.log(collection) it will output collection[0] and collection[1]. In this case I don't know what which value collection[1] is.
Question
How can I, like my first example, have something like named dynamically arguments like collection['second'] or similar?
As we want to access the value dynamically, set collection to an empty object first. Then, use the keys from collection to pass all its Promise-values to Promise.all. Then, map back the fulfilled values and then, we can access collection's value by some key.
let collection = {}
for (let i = 0; i < 3; i++) {
collection[`key${i}`] = Promise.resolve(i)
}
let collectionKeys = Object.keys(collection)
Promise.all(collectionKeys.map(key => collection[key]))
.then(values => {
let collectionFulfilled = collectionKeys.reduce((obj, key, i) => {
obj[key] = values[i]
return obj
}, {})
console.log(collectionFulfilled)
})
If you pass your promises embedded inside an object with a single key, you could use that for it's name, and then with a simple helper function reverse the values & keys from this.
With the new ES6 you can then just pass like -> [{one}, {two}, {three}] etc.
Below is an example with a helper function called namedPromiseAll.
function namedPromiseAll(named) {
const pcollection =
named.map(m => Object.values(m)[0]);
const ncollection =
named.map(m => Object.keys(m)[0]);
return Promise.all(pcollection).then((c) => {
return c.reduce((a,v,ix) => {
a[ncollection[ix]] = v;
return a;
}, {});
});
}
const second = Promise.resolve(2);
const third = Promise.resolve(3);
const collection = [{second}, {third}];
namedPromiseAll(collection).then(console.log);
Is there any benefit to checking if an array has length before using forEach?
Consider the following:
const foo = () => {
const elements = [...document.querySelectorAll(".selector")];
elements.forEach(element => {
element.style.height = `${element._someProperty}px`;
});
};
This is what I have in my project. In some cases the elements array will be empty because the existence of such elements is based on user-input.
My question is:
Should I add something like
if (!elements.length) return;
before calling the forEach method like so
const foo = () => {
const elements = [...document.querySelectorAll(".selector")];
if (!elements.length) return;
elements.forEach(element => {
element.style.height = `${element._someProperty}px`;
});
};
Or would that be considered a micro-optimization?
Let's start with a definition: A transducer is a function that takes a reducer function and returns a reducer function.
A reducer is a binary function that takes an accumulator and a value and returns an accumulator. A reducer can be executed with a reduce function (note: all function are curried but I've cat out this as well as definitions for pipe and compose for the sake of readability - you can see them in live demo):
const reduce = (reducer, init, data) => {
let result = init;
for (const item of data) {
result = reducer(result, item);
}
return result;
}
With reduce we can implement map and filter functions:
const mapReducer = xf => (acc, item) => [...acc, xf(item)];
const map = (xf, arr) => reduce(mapReducer(xf), [], arr);
const filterReducer = predicate => (acc, item) => predicate(item) ?
[...acc, item] :
acc;
const filter = (predicate, arr) => reduce(filterReducer(predicate), [], arr);
As we can see there're a few similarities between map and filter and both of those functions work only with arrays. Another disadvantage is that when we compose those two functions, in each step a temporary array is created that gets passed to another function.
const even = n => n % 2 === 0;
const double = n => n * 2;
const doubleEven = pipe(filter(even), map(double));
doubleEven([1,2,3,4,5]);
// first we get [2, 4] from filter
// then final result: [4, 8]
Transducers help us solve that concerns: when we use a transducer there are no temporary arrays created and we can generalize our functions to work not only with arrays. Transducers need a transduce function to work Transducers are generally executed by passing to transduce function:
const transduce = (xform, iterator, init, data) =>
reduce(xform(iterator), init, data);
const mapping = (xf, reducer) => (acc, item) => reducer(acc, xf(item));
const filtering = (predicate, reducer) => (acc, item) => predicate(item) ?
reducer(acc, item) :
acc;
const arrReducer = (acc, item) => [...acc, item];
const transformer = compose(filtering(even), mapping(double));
const performantDoubleEven = transduce(transformer, arrReducer, [])
performantDoubleEven([1, 2, 3, 4, 5]); // -> [4, 8] with no temporary arrays created
We can even define array map and filter using transducer because it's so composable:
const map = (xf, data) => transduce(mapping(xf), arrReducer, [], data);
const filter = (predicate, data) => transduce(filtering(predicate), arrReducer, [], data);
live version if you'd like to run the code -> https://runkit.com/marzelin/transducers
Does my reasoning makes sense?
Your understanding is correct but incomplete.
In addition to the concepts you've described, transducers can do the following:
Support a early exit semantic
Support a completion semantic
Be stateful
Support an init value for the step function.
So for instance, an implementation in JavaScript would need to do this:
// Ensure reduce preserves early termination
let called = 0;
let updatesCalled = map(a => { called += 1; return a; });
let hasTwo = reduce(compose(take(2), updatesCalled)(append), [1,2,3]).toString();
console.assert(hasTwo === '1,2', hasTwo);
console.assert(called === 2, called);
Here because of the call to take the reducing operation bails early.
It needs to be able to (optionally) call the step function with no arguments for an initial value:
// handles lack of initial value
let mapDouble = map(n => n * 2);
console.assert(reduce(mapDouble(sum), [1,2]) === 6);
Here a call to sum with no arguments returns the additive identity (zero) to seed the reduction.
In order to accomplish this, here's a helper function:
const addArities = (defaultValue, reducer) => (...args) => {
switch (args.length) {
case 0: return typeof defaultValue === 'function' ? defaultValue() : defaultValue;
case 1: return args[0];
default: return reducer(...args);
}
};
This takes an initial value (or a function that can provide one) and a reducer to seed for:
const sum = addArities(0, (a, b) => a + b);
Now sum has the proper semantics, and it's also how append in the first example is defined. For a stateful transducer, look at take (including helper functions):
// Denotes early completion
class _Wrapped {
constructor (val) { this[DONE] = val }
};
const isReduced = a => a instanceof _Wrapped;
// ensures reduced for bubbling
const reduced = a => a instanceof _Wrapped ? a : new _Wrapped(a);
const unWrap = a => isReduced(a) ? a[DONE] : a;
const enforceArgumentContract = f => (xform, reducer, accum, input, state) => {
// initialization
if (!exists(input)) return reducer();
// Early termination, bubble
if (isReduced(accum)) return accum;
return f(xform, reducer, accum, input, state);
};
/*
* factory
*
* Helper for creating transducers.
*
* Takes a step process, intial state and returns a function that takes a
* transforming function which returns a transducer takes a reducing function,
* optional collection, optional initial value. If collection is not passed
* returns a modified reducing function, otherwise reduces the collection.
*/
const factory = (process, initState) => xform => (reducer, coll, initValue) => {
let state = {};
state.value = typeof initState === 'function' ? initState() : initState;
let step = enforceArgumentContract(process);
let trans = (accum, input) => step(xform, reducer, accum, input, state);
if (coll === undefined) {
return trans; // return transducer
} else if (typeof coll[Symbol.iterator] === 'function') {
return unWrap(reduce(...[trans, coll, initValue].filter(exists)));
} else {
throw NON_ITER;
}
};
const take = factory((n, reducer, accum, input, state) => {
if (state.value >= n) {
return reduced(accum);
} else {
state.value += 1;
}
return reducer(accum, input);
}, () => 0);
If you want to see all of this in action I made a little library a while back. Although I ignored the interop protocol from Cognitect (I just wanted to get the concepts) I did try to implement the semantics as accurately as possible based on Rich Hickey's talks from Strange Loop and Conj.