JS - Map/Array choice when both can do the same task - javascript

This is probably more of an opinion based question. But recently on a project I have seen that Map is always used over array.
My question is when would you ever use an array over a Map?
Note I mean in cases where an array would work as well as a Map.
i.e. storing data, checking it exists, and deleting it.
example here:
// Array
const teams = [];
if (teams.includes(team.id)) {
return;
}
teams.push(team.id);
// do stuff
teams = teams.filter(
(id) => id !== team.id
);
// Map
const teams = new Map();
if (teams.has(team.id)) {
return;
}
teams.set(team.id, team.id);
// do stuff
teams(team.id);
As I understand Map is more performant, you also get methods like get, set, delete which are useful.
If faced with the task above what would people use and why?

Related

Trying to create a generic, re usable function for set of different statuses

I have a function that I'm including on each line of my component that looks at and filters off of the returned number of records with a specific status and then takes the length of that array and displays it on the screen.
{props.locations.filter(x => x.details && x.details.status === "NEVER").length}
Currently there are only 3 separate statuses that a location could possibly have. Because of that, I'm trying to create a separate function that looks at whatever status is passed in instead of hard coding it for each line in my component. I recognize that this might be a really basic question, but does any have any idea how to do this?
The nicest is to able to partially apply status, so lets put it as a first argument
const filter = status => locations => {
return locations.filter(x => x.details && x.details.status === status).length;
}
Now we can make nice filters for every status
const neverFilter = filter('NEVER');
const progressFilter = filter('PROGRESS')
// and use it
const results = neverFilter(locations);
If you need it can be used also in one line, so its very flexible construct
const results = filter('NEVER')(locations);
PS. I didn't append any TS types as you did not put any in your question, but if u use TS, status should be some kind of union like type Status = 'NEVER' | 'Other', and location should be kind of Record type. Just saying :)
Something like this:
filter = (locations, status) => {
return locations.filter(x => x.details && x.details.status === status).length;
}
And call the function:
this.filter(props.locations, 'NEVER');

More efficient way to handle an extremely specific response from a Rest API in JavaScript

I'm working with a Dungeons & Dragons 5e API and want that an especific result be treated in a special way. The user can choose what to search from a range of options, and in only one of them would I need to take care of the answer in a different way. In this option, I get the answer in JSON that contains a 'name' field, which stores a String, but in this specific case this String comes with an acronym, and I would like to transform it into the full name.I'm afraid to just put am 'if' statement in the middle of the code and deal with the situation inefficiently, even more so that I did not find similar situations to have any reference.
This is part of the result of the API I want to handle in a special way:
{"count":6,
"results":[
{"name":"STR",
"url":"http://www.dnd5eapi.co/api/ability-score/1"},
{"name":"DEX",
"url":"http://www.dnd5eapi.co/api/ability-scores2"},
....
]
}
This is how I handle the answer:
fetch(fullAPIURL)
.then(result => result.json())
.then(data => {
let resultContainer = document.getElementById('resultContainer');
//Cleaning the result container from previous results
document.querySelectorAll('#resultContainer article').forEach(container =>
resultContainer.removeChild(container));
spanSearchResult.classList.remove('invisible', 'searchFail');
spanSearchResult.classList.add('searchSucess');
spanSearchResult.innerHTML = `Search returned ${data.count} results`;
for (element of data.results) {
let containerTitle = element.name != undefined ? element.name : element.class;
resultContainer.appendChild(createResultContainer(containerTitle));
}
})
.catch(err => {
spanSearchResult.classList.remove('invisible');
spanSearchResult.classList.add('searchFail');
spanSearchResult.innerHTML = 'Something went wrong! Details in the console';
console.log(err);
});
Is putting a condition in this snippet of code really the most efficient way to solve this situation?
Thanks in advance.
You could just make a lookup call, actually. In fact, that'd be preferable if you ever want to port your application to another language, for example.
Define the following:
var retrieve = (function() {
var items = {
"STR": "Strength",
"DEX": "Dexterity"
};
return function(item) {
return items[item] || item;
}
})();
console.log(retrieve("DEX"));
With this, you can simply call retrieve(element.name) to retrieve its "actual" name. You can add elements to the object to create new translations, and if you ever need to support multiple languages, you can even replace the function entirely.

getAllData does not include element just inserted, even after callback

So I am using NeDB as a data store for a simple little project, and I want to create a little API that inserts something new and returns the list of all items in the datastore, including the new one.
async function willAddNewItem() {
// db is NeDB Datastore
const existing = db.getAllData();
const sortedIds = existing
.map(item => item.id)
.sort((one, another) => one - another);
const id = sortedIds.length === 0
? 0
: sortedIds.slice(-1)[0] + 1;
const newItem = { id, foo: 'bar' };
await new Promise(resolve => db.insert(newItem, () => resolve()));
return db.getAllData()
.sort((one, another) =>
new Date(one.updatedAt).getTime() - new Date(another.updatedAt).getTime()
);
}
However, every time I call this function, I get the list of items I had before inserting the new one. On the next call, the item I added last time would be there, but not the new one. Refreshing my page (which results in calling db.getAllData() again to populate the initial page) shows everything it should. It’s only missing immediately after I insert it. So it would appear that the callback on db.insert is not actually waiting until the insertion is complete (nor is there any documentation that I can find about this).
Adding an index to id fixes this, and I do want an index on id so that is good, but I still want to know why this is happening/where I have to be careful about it happening in the future. For that matter, I’m a little worried that the only reason adding the index to id worked is because it happened to be a little faster, so it was “done” before I called db.getAllData() when it wasn’t before—and that this may not be consistent.
I could use something like
const afterAdding = await new Promise(resolve =>
db.insert(newItem, (_, newDoc) => resolve([...existing, newDoc]))
);
and return the sorted afterAdding, but this seems a little dubious to me (I’d really like to get exactly the db’s state rather than try to “recreate” it myself, assuming that insert has done exactly and only what I expected), and in any event I would like to know more about how NeDB works here.
Also, yes, I know I’m ignoring possible errors on the insert (I have checked to see if this is the cause; it is not), and I probably don’t want .getAllData() at all but rather some .find query. This is a basic test case as I get familiar with NeDB.

Write pre-determined node id when pushing to Firebase array

This question has been asked before, but none of the solutions provided seem to help my problem:
I have an array of data on Firebase, that I loop through to generate content in my app, using a "for" loop and therefore an index value. This works fine with the predetermined sets of data, as the IDs are simple array numerics but when a user adds some new data, which is added to the array with push(), firebase creates a unique node key (such as -KyWRU7RRCE_V1w_OiZx) for the new set of data which for some reason prevents my loop from working (no errors shown in console).
If I go to firebase and manually ensure that the new set of data has a numeric value, loop starts working again. How do I make it so that when the user pushes new data to the array, the key being generated is numeric? I tried this:
// Push new routine to Firebase.
var firebaseRef = firebase.database().ref();
var createdExercise = JSON.parse(localStorage.newRoutine);
$("#save").click(function()
{
firebaseRef.child("workouts").key(some_indexed_value).push(createdExercise);
});
but the console returns the following error: Uncaught TypeError: firebaseRef.child(...).key is not a function.
In case it is useful, here is the loop I am using:
// Initialize Firebase.
firebase.initializeApp(config);
// Reference data.
var dbRef = firebase.database().ref().child("workouts");
// Sync with Firebase in real time.
dbRef.on("value", snap =>
{
var workouts = snap.val();
for (var i = 0; i < workouts.length; i++)
{
var routine = workouts[i].title;
var time = 3;
var exercises = workouts[i].exercises;
var icons;
$("#cardList").append(createCards(routine));
}
});
And a pic of my JSON data on Firebase:
Answer to your question is using .set() instead of .push() like this
firebaseRef.child("workouts").child(some_indexed_value).set(createdExercise);
this will create a node under workouts with your custom key.
but it is recommended to use push keys and use forEach() method to get the result than using for loop

Firebase Cloud Function database snapShot

I am trying to write a Firebase Cloud Function that takes all the users from my database and edits values within them. However, in order to do that I need a quick way for the code in my Cloud function to receive all of the user-ids from the database. For now, I have the function print the data it gets from the database so I know what I am working with.
The result is this:
{"WNGOuQqZhZSo5UFovgmgEAVX3Gz1":{"displayName":"jack","email":"test2#gmail.com"},"aRaZVJorkYNwCSzAbMkNJiGwzJm2":{"displayName":"testing","email":"testing#gmail.com"}}
I just need the two userIds instead.
This is the part of database I am working with:
This is the code:
If anyone knows what I need to change I would really appreciate it! Thanks!
I think you need to have a for - in loop over value. Below is the pseudocode -
var value = snapShot.val();
let namesArray = [];
for (var key in value) {
if (value.hasOwnProperty(key)) {
namesArray.push(key);
}
}
As with what jaibatrik said, you need to iterate over your objects.
You can't get the key without fetching the whole object.
In addition to running a for loop you can run a forEach over the snapshot.
const uids = [];
snapShot.forEach(single => {
uids.push(single.key);
});
The other option is to get all the keys from the snapShot.val() object.
const value = snapShot.val();
const uids = Object.keys(value);
Both ways are effectivly the same thing, just a preference on which way you prefer to do things

Categories