Reduce an Array that has objects - javascript

I'm new to javascript, and I'm trying to learn more about working with array's so I built a grocery list builder, and I'm getting stuck on reducing the list if the user accidentally inserts an item twice.
I think the problem is that my reducing function isn't hitting the item selector, but I haven't been able to find any examples showing this kind of problem.
This is what I have for a reducing function:
function reducingFood() {
var reducedFood = myGroceries.reduce(function(obj, item) {
var itemName = item['item'];
if(!obj[itemName]) {
obj[itemName] = 0;
}
obj[itemName]++;
return obj;
}, {})
addStructure();
}
I setup a JSFiddle showing the full code:
https://jsfiddle.net/Jon43/frvLc7Le/8/

Related

Select and deselect multiple items from array

I have a snippet of code here where i have an array that may or may not have keys in it. When the user presses on a 'friend' they add them to a list (array) where they might start a chat with them (add 3 friends to the array, then start a chatroom). The users selected might be toggled on or off.
Current Behavior:
i can add/remove one person, but i cant add multiple people to the array at the same time. When i add one person, select another - the first person is 'active', when i remove the first person, the second person automatically becomes active
Expected Behavior:
I would like to be able to add multiple people to the array and then remove any of the selected items from the array
onFriendChatPress = (key) => {
console.log(key) // this is my key 'JFOFK7483JFNRW'
let friendsChat = this.state.friendsChat // this is an empty array initially []
if (friendsChat.length === 0) {
friendsChat.push(key)
} else {
// there are friends/keys in the array loop through all possible items in the array to determine if the key matches any of the keys
for (let i = 0; i < this.state.selGame.friends.length; i++) {
// if the key matches, 'toggle' them out of the array
if (friendsChat[i] === key) {
friendsChat = friendsChat.filter(function (a) { return a !== key })
}
else {
return friendsChat.indexOf(key) === -1 ? friendsChat.push(key) :
}
}
}
}
Help please!
From your code, I was quite confused regarding the difference between this.state.selGame.friends and this.state.friendsChat. Maybe I missed something in your explication. However, I felt that your code seemed a bit too overcomplicated for something relatively simple. Here's my take on that task:
class Game {
state = {
friendsChat: [] as string[],
};
onFriendToggle(key: string) {
const gameRoomMembers = this.state.friendsChat;
if (gameRoomMembers.includes(key)) {
this.state.friendsChat = gameRoomMembers.filter(
(member) => member !== key
);
} else {
this.state.friendsChat = [...gameRoomMembers, key];
}
}
}
I used typescript because it makes things easier to see, but your JS code should probably give you a nice type inference as well. I went for readability over performance, but you can easily optimize the script above once you understand the process.
You should be able to go from what I sent you and tweak it to be according to what you need

Filter an Array of Objects from an Array in TypeScript

I built a custom component that filters an array of objects. The filter uses buttons, sets from active to non-active and allows more than one option on/off at the same time.
StackBlitz of my attempt - https://stackblitz.com/edit/timeline-angular-7-ut6fxu
In my demo you will see 3 buttons/options of north, south and east. By clicking on one you make it active and the result should include or exclude a matching "location" either north, south and east.
I have created my methods and structure to do the filtering, I'm struggling with the final piece of logic.
So far I have created a method to create an array of filtered locations depending on what the user clicks from the 3 buttons.
Next this passes to my "filter array" that gets the logic that should compare this filtered array against the original to bring back the array of results that are still remaining.
Its not quite working and not sure why - I originally got this piece of functionality working by using a pipe, but fore reasons do not want to go in that direction.
//the action
toggle(location) {
let indexLocation = this.filteredLocations.indexOf(location);
if (indexLocation >= 0) {
this.filteredLocations = this.filteredLocations.filter(
i => i !== location
);
} else {
this.filteredLocations.push({ location });
}
this.filterTimeLine();
}
// the filter
filterTimeLine() {
this.filteredTimeline = this.timeLine.filter(x =>
this.contactMethodFilter(x)
);
}
//the logic
private contactMethodFilter(entry) {
const myArrayFiltered = this.timeLine.filter(el => {
return this.filteredLocations.some(f => {
return f.location === el.location;
});
});
}
https://stackblitz.com/edit/timeline-angular-7-ut6fxu
Sorry for my expression but u have a disaster in your code. jajaja!. maybe u lost that what u need but the logic in your functions in so wrong. comparing string with objects. filter a array that filter the same array inside... soo u need make a few changes.
One:
this.filteredLocations.push({location});
Your are pushing object. u need push only the string.
this.filteredLocations.push(location);
Two:
filterTimeLine() {
this.filteredTimeline = this.timeLine.filter(x =>
this.contactMethodFilter(x)
);
}
in this function you filter the timeLine array. and inside of contactMethodFilter you call filter method to timeLine again....
See a functional solution:
https://stackblitz.com/edit/timeline-angular-7-rg7k3j
private contactMethodFilter(entry) {
const myArrayFiltered = this.timeLine.filter(el => {
return this.filteredLocations.some(f => {
return f.location === el.location;
});
});
}
This function is not returning any value and is passed to the .filter
Consider returning a boolean based on your logic. Currently the filter gets undefined(falsy) and everything would be filtered out

Finding elements in an array of objects where you have a bigger key

I'm writing a tiny reactive framework where I need to find out which subscriber needs updating. I'm implementing deep binding and I'm running into a wall how to find subscribers in an effective manner.
A stored variable can be an object, so for example
{
"user": {
"preferences": {
"food": "vegetarian"
}
}
}
You can get content to any level of this variable like this
getVar("user_preferences_food");
getVar("user_preferences");
However, you can also update it like that
setVar("user_preferences_food", "meat");
setVar("user_preferences", {"food": "meat"});
But in case of the first setVar (user_preferences_food) how can I find the subscriber using getVar("user_preferences"); or even getVar("user"); most effectively.
I already got it working by splitting the var on _ and then one by one concatting the next level and merging all the resulting arrays. But this is very resource intensive. Especially if there are a lot of subscribers. There must be a better way to find them that is less resource intensive.
Edit: I left out part of the explanation.
There is a subscribe method too
subscribe("user", cb);
subscribe("user_preferences", cb);
subscribe("user_preferences_food", cb);
These subscriptions are stored in an array in the framework.
As soon as "user_preferences_food" is updated for example, all subscriptions above should be triggered. But obviously not subscribe('othervar');
simplification of the subscribe method:
var subscriptions = [];
function subscribe(var, callback){
subscriptions.push({var: var, cb: callback});
}
Simplification of getVar
vars = {};
getVar(var){
// find var in vars with this logic: https://stackoverflow.com/a/18937118/249710
// current exact match on subscribers, but need the "parents, grandparents etc here
var toUpdate = _.where(subscriptions, {
"var" : var
});
_.each(toUpdate, function(sub){ sub.cb();});
}
Storing or getting data as part of the key I've already got covered. It is just finding the subscribers in the most effective manner
ps: this is in an environment where I cannot rely on ES6 yet (not all users have it enabled), there is no DOM but I do have underscore included. (Titanium app development platform)
I would try to make a list for the callbacks, so you loop trough one list so you dont have to search, because you know the list is there with all the callbacks.
So if you call setVar('user_prefs') you set a seperate list with the root var. in this case its the user.
if any object is changed with setVar (in depth or not) you go to you're root var, get the list and loop trough this list with the callbacks.
The beauty of this is you can set a list with the root var,
var cbList[FIRSTVAR] this contains all the callbacks. No searching just loop.
Its the mongoDb principle, the data is ready to go, you don't search because you know the list is already there.
You could split the string and use it for reduceing the object.
function getVar(object, path) {
return path
.split('_')
.reduce(function (o, k) {
return (o || {})[k];
}, object);
}
function setVar(object, path, value) {
var keys = path.split('_'),
last = keys.pop();
keys.reduce(function (o, k) {
return o[k] = o[k] || {};
}, object)[last] = value;
}
var object = { user: { preferences: { food: "vegetarian" } } };
console.log(getVar(object, "user_preferences_food"));
console.log(getVar(object, "user_preferences"));
setVar(object, "user_preferences_food", "meat");
console.log(object);
setVar(object, "user_preferences", {"food": "meat"});
console.log(object);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I ended up doing this:
var options = [];
var parts = key.split('_');
var string = parts[0];
_.each(parts, function(p, i){
if (i > 0) string += '_' + p;
options.push(string);
});
var toUpdate = _.filter(subscribers, function(sub){
if (sub.var.indexOf(key + '_') === 0) return true;
if (options.indexOf(sub.var) > -1) return true;
return false;
});
So checking with indexOf on the string to see if there are children. And building an array with parents so any layer is a match, and doing an indexOf on that as well. I think this is the least complicated method of implementing it

Cant remove duplicate items in collection in VueJS

I am trying to remove duplicate items from a collection that I request via an API in Laravel.
This is my code:
computed: {
// slice the array of data to display
filteredList() {
/* NEW PART */
var tips = this.dublicate;
/* END NEW PART */
tips = this.items.filter(item => {
return item.tip.toLowerCase().includes(this.search.toLowerCase())
})
return tips.slice(0, this.display);
},
dublicate() {
var filtered_array = [];
for(var i =0; i < this.items.length; i++) {
if(this.items[i].tip.toLowerCase() != this.items[i+1].tip.toLowerCase()) {
filtered_array.push(this.items[i])
}
}
return filtered_array;
}
}
}
If I remove the code within the NEW PART comments, everythin works fine.
In the NEW PART I am trying to remove duplicate content, based on the items tip attribute.
If the tip attribute is the same as the next items tip attribute, it should be excluded from the tips array, which is returned as a v-for="tips in filteredList".
However, I just get an empty array with this new part. What am I doing wrong?
I get the following from Vue Devtools:
dublicate:"(error during evaluation)"
filteredList:"(error during evaluation)"
An example data from items, that are from an API request:
(This is the data that I get, when I dont try to remove duplicates, which works)
As this is in VueJS, I cant use the answer provided here.
You are looking past the end of the array with i + 1. You need to push the last item without looking for the one after it (because there isn't one). I think using filter is more straightforward than building an array with a for loop.
dublicate() {
return this.items.filter((a, i) =>
i === this.items.length - 1 ||
a.tip.toLowerCase() !== this.items[i + 1].tip.toLowerCase()
);
}

Using Firebase Push key as Key in Second Push

I'm trying to add two related items to my Firebase database. I want to push one item, then get that item's newly created key and use it as the key for the second item in a different tree. I've tried querying the database to get the last key created and using it as the key for the second push, but it's still just generating a new key for it. Here's the code that I'm using:
save: function() {
if (this.$.document.isNew && (this.editableCard.title || this.editableCard.body)) {
return this.$.document.save(this.cardsPath).then(function() {
this.$.document.reset();
var sceneRef = firebase.database().ref().child(this.cardsPath);
var scene = sceneRef.orderByKey().limitToLast(1);
var sceneKey = scene.key;
this.$.document.save('/documents/', sceneKey);
}.bind(this));
}
return Promise.resolve();
}
(I'm using Polymer, and my starting point is the note-app demo for Polymerfire).
Any ideas on how I can retrieve the new key of the first push and use it for the second push? Thanks!
EDIT
I found the answer in Firebase's documentation for Reading and Writing to the database for Web. Link
push() returns a DatabaseReference immediately. You can ask that reference what its key is, using getKey(), then use that string to update another location in your database.
You can access the key property on the original database reference and use that as the key for the second one, like so:
let firstObjRef = firebase.database().ref('/first/path/).push(firstObj, (error) => {
videoObj["roomUploadedTo"] = this.roomName;
var updateObj = {};
updateObj[videoObjRef.key] = videoObj;
firebase.database().ref('/second/path/').update(updateObj).then( (e) => {
console.log('update went through. booyah! ' + e);
})

Categories