Testing conditions on a constantly changing object in javascript - javascript

I'm trying to create a browser game. The game state is stored in an object with multiple layers.
let State = {
points: 0,
health: 50,
currentLocation: {x: 5, y: 55},
inventory: { bread: 8, water: 33, money: 20 }
abilities:
charisma:5,
perseverance: 3,
speed: 8
}
buildings {
bakery: { location: {x: 23, y: 41}, unlocked: 1, visited: 1},
homestead: { location: {x: 3, y: 59}, unlocked: 0, visited: 0},
forge: { location: {x: 56, y: 11}, unlocked: 1, visited: 0}
}
}
I want to be able to control the game logic based on the current values of State.
Some cases are very simple
if(State.health == 0) { Game.die(); }
Most cases are much more complex
if(State.buildings.bakery.unlocked == 1) {
// calculate player's distance to bakery
let dX = Math.abs(State.buildings.bakery.location.x - State.currentLocation.x)
let dY = Math.abs(State.buildings.bakery.location.y - State.currentLocation.y)
let d = DX + dY;
if(State.inventory.bread == 0 && d < State.inventory.abilities.speed) {
Game.die();
}
}
What is the best way to achieve something like this? Looping over all the conditions seems to be a needless use of resources. I've looked into getters, setters and Proxy but don't really know what I'm doing! Ideally I'd only want to only check the logic when a relevant part of State changes.

I'd only want to only check the logic when a relevant part of State changes.
Then use a getter and setter like this:
let State = {
buildings: {
bakery: {
_unlocked: false,
get unlocked() {
return this._unlocked;
},
set unlocked(value) {
this._unlocked = value;
if (value == true) {
console.log("Calculating player's distance to bakery ...");
}
}
}
}
}
State.buildings.bakery.unlocked = true;
// Calculating player's distance to bakery ...
The use of the _unlocked variable is because if you didn't, the first time unlocked is accessed, it will trigger its own getter recursively until you get a stack overflow error.

Here's what I've ended up doing...
const state = {
_data:{}, // object in which the data is actually stored
get:(path) => { // safely get the value or return undefined
return path.split('.').reduce((o,i)=>(o||{})[i], state._data)
},
set:(fullpath,value,silent=0) => { // safely set the value
function cycle(obj,path,value) {
let i = path[0]
if(path.length==1) { // if this is the end of the path
obj[i] = value; // change the value
if(!silent) { Pubsub.publish('state.'+fullpath, value) ; } // and publish the event
} else { // if this is not the end of the the path
if(typeof obj[i] !== 'object') { obj[i] = {} } // create this part of the path if it doesn't exist
cycle(obj[i],path.slice(1), value) // move on to the next part of the path
}
}
cycle(state._data,fullpath.split('.'),value)
}
Step 1: Get and set
I created two custom functions to get and set the state.
get() takes a dot notation path as a string, e.g. state.get('buildings.bakery.unlocked').
set() also takes a dot notation path as a string as well as the value, e.g. state.set('buildings.bakery.unlocked', 1).
I used some code from this thread. Using these functions means it's easy to manipulate nested properties without worrying about the dreaded TypeError.
Step 2: PubSub
The dot notation path also feeds a publish/subscribe model. I'm using PubSubJS. Calling set() also publishes an event that matches the path, e.g. state.set('abilities.strength', 5) also publishes 'state.abilities.strength'.
To monitor state changes, you simply subscribe to the relevant events:
PubSub.subscribe('state.abilities.strength', () => console.log('strength value changed'))
The published event also passes back the new value, so you can do what you want with it:
PubSub.subscribe('state.inventory.bread', (path, value) => console.log('bread value changed, you now have ' + value))
Step 3: Implement to compare
PubSub has the benefit of being a topic based system. This means you can subscribe to non-final nodes:
PubSub.subscribe('state.inventory', () => console.log('something in the inventory changed'))
PubSub.subscribe('state.inventory.water', () => console.log('water changed'))
Changing inventory.bread will trigger the first, changing inventory.water will trigger both.
This allows me to separates the game logic from the state. There's two ways to compare values...
// function to compare two values
let comparison = function () {
console.log('running comparison');
if(state.get('test.valueA') > state.get('test.valueB')) {
console.log('A is bigger than B');
}
}
// you either subscribe to both the values, and compare whenever either one changes
PubSub.subscribe('state.test.valueA', comparison)
PubSub.subscribe('state.test.valueB', comparison)
// or alternatively, if they have a shared topic, then subscribe to that
PubSub.subscribe('state.test', comparison)

Related

How can I implement a function that gets rid of reduntant Objets inside an Array of Objects in JavaScript?

So I am trying to implement a function that allows me to stock locally details of a couch inside a JS object with 3 properties:
An ID (which is covered with the URL of the product through a function)
A color (which is recovered thanks to an event listener)
A quantity (which is recovered the same way as a color)
I already have the function that allows me to stock objects locally with an array:
function addedToCart(productObject) {
let listOfProducts = getProducts();
listOfProducts.push(productObject);
registerProducts(listOfProducts);
}
function getProducts() {
let listOfProducts = localStorage.getItem("listOfProducts");
if (listOfProducts == null) {
return [];
} else {
return JSON.parse(listOfProducts);
}
}
function registerProducts(listOfProducts) {
localStorage.setItem("listOfProducts", JSON.stringify(listOfProducts));
}
To finish, I have a 3 event listeners:
To listen when the user selects an option and get the value of the color
To listen to the change of the <input type="number"/> and get the value of the quantity
To listen when the user clicks on the "Add to cart" button
Here's my code in JS
class classProductCartDetails {
constructor(id, color, quantity) {
this.id = id;
this.color = color;
this.quantity = quantity;
}
}
let quantityProduct = document.getElementById("quantity");
let valid = true;
let colorValue = 0;
let quantityValue = 0;
itemColors.addEventListener("change", function (event) {
colorValue = event.target.value;
console.log(colorValue);
});
quantityProduct.addEventListener("input", function (event) {
quantityValue = event.target.value;
console.log(quantityValue);
});
addToCartButton.addEventListener("click", function () {
if (quantityValue > 0 && quantityValue <= 100 && colorValue != 0) {
let objectProduct = new classProductCartDetails(
productId,
colorValue,
quantityValue
);
console.table(objectProduct);
alert("Your product has been added to the cart!");
addedToCart(objectProduct);
}
if(colorValue == 0 && quantityValue <= 0){
alert("WARNING! You must add the details of the product");
}
else if (colorValue == 0) {
alert("WARNING! You must add a color");
}
else if (quantityValue <= 0) {
alert("WARNING! The quantity inputted is invalid");
}
});
The problem? If I just click twice on the "Add to cart" button or if I change the quantity of the couch then click the button, it's going to add a redundant new object instance to the array!
[{id: "107fb5b75607497b96722bda5b504926", color: "White", quantity: "1"},…]
0: {id: "107fb5b75607497b96722bda5b504926", color: "White", quantity: "1"}
1: {id: "107fb5b75607497b96722bda5b504926", color: "Blue", quantity: "1"}
2: {id: "107fb5b75607497b96722bda5b504926", color: "Blue", quantity: "1"}
3: {id: "107fb5b75607497b96722bda5b504926", color: "Blue", quantity: "2"}
As you can see, we have 3 times the same object, 2 that are the same and one that has a different quantity.
And I want to implement a function that allows me to check individually the ID, color and quantity of an object with those of another object in the array.
With these conditions:
If the ID, Color and Quantity are the same → Delete the redundant object in
question
If only the ID and the Color are the same but the quantity is
different → Replace the value of quantity of the non-redundant with the latest value which is on the redundant object and delete the redundant object
For instance, the first object properties with the second, then with the third... then the second object with the first, the second with the third one... and so on and so forth until the last object of the array.
English isn't my native language, so you might not understand what I meant to say, but no worries, so here's a visual representation with this photo:
Also TD:DR
Words in picture:
-No redundancy
-Redundancy detected:
1. Eliminated object with index 2 and 3
2. Changed the quantity of object with index 1 from 1 to 2
And that would be the result in JSON on the local storage:
[{id: "107fb5b75607497b96722bda5b504926", color: "White", quantity: "1"},…]
0: {id: "107fb5b75607497b96722bda5b504926", color: "White", quantity: "1"}
1: {id: "107fb5b75607497b96722bda5b504926", color: "Blue", quantity: "2"}
I've already looked at some posts on how to get rid of redundant objects in an array but they were static arrays
Is it possible to implement such a function (either through recursion or iteration)? Is it too far-fetched? Are there easier ways to get rid of redundant objects inside an array?
Any help would be appreciated
Btw if you have any recommendations to give me on my code OR have any question on my code, don't hesitate!
You could do
function addedToCart(productObject) {
let listOfProducts = getProducts();
listOfProducts = listOfProducts.filter(({productId, colorValue})=> {
return !(productId == productObject.productId && colorValue == productObject.colorValue);
})
listOfProducts.push(productObject);
registerProducts(listOfProducts);
}
With this, if productId and colorValue are same, the previous entry is removed from the list. It will be replace by the new productObject. Whether quantity is same or not does not really matter: in all cases, you want to take the quantity from productObject.
So I actually found a solution:
let verifyProducts = (objectToVerify, arrayOfObjects) => {
let verifyProduct = arrayOfObjects.find((object) => {
const {id, color} = objectToVerify;
object.id === id && object.color === color
});
if(verifyProduct){
verifyProduct.quantity = Number(objectToVerify.quantity);
}else{
arrayOfObjects.push(objectToVerify);
}
}
here you go ... pass your array to the function and the function will return an array with the redundant objects deleted.
we basically iterate through the array starting at index 0 (so the oldest entries first).
For each index we check, if the combination of our current objects' id and color exists further along in the array (which if true would mean one of two things:
it exists an object, that has the same id, color and quantity
or an object exists, that has the same id and color but different quantity
and since we only compare against objects that are "newer" than our current object, both cases would mean, that the object at our current index is redundant ... so we just delete it.
after our loop we tidy up our array by removing all those indexes which are now undefined
If you were to implement that code from the start and using it every time when adding a new object, you could get away with only checking against the last object
const arr = [{id : 1, color : "black", quantity: 1},
{id : 1, color : "black", quantity: 1},
{id : 1, color : "black", quantity: 2},
{id : 2, color : "black", quantity: 1}];
function getRidOfRedundant(arr) {
let newArr = [...arr] //creating shallow copy of arr
let compare = newArr.map(x => x.id + x.color);
for (let i = 0; i < compare.length; i++) {
if(compare.includes(compare[i], i + 1)) {
delete (newArr[i])
}
}
return newArr.filter(x => x !== undefined)
}
console.log(getRidOfRedundant(arr))
there is also the possibility to intervene directly at object creation. Have a condition in your constructor function, that replaces the object in the array when id and color are the same (I'm using static Properties of the class here, since using "outside" variables/objects in the constructor can be kinda sketchy) - don't know though how this would interact/work with your localStorage stuff
and you could also just take the logic stuff from the constructor and create a function, that does basically the same thing (finding index of now redundant object and overwrite it with the newly created Object)
class Product {
static memory = [];
static objects = []
constructor(id, color, quantity) {
this.id = id;
this.color = color;
this.quantity = quantity;
if (Product.memory.includes(id + color)) {
Product.objects[Product.memory.indexOf(id + color)] = this;
} else {
Product.memory.push(id + color);
Product.objects.push(this);
}
}
}
new Product(id, color, quantity)

Is there a way that I can dynamically pass object names to a function (as arguments) and reference properties of that object using the argument?

I am trying to get the ID from imgs in my HTML, and pass those id's (which correspond to js objects that I've created) as arguments then access certain properties of those objects inside a function.
I've tried a variety of different methods to select the id of the child element, but I still get 'undefined' when I run the function because for some reason, passing the id as the argument doesn't allow me to access the keys of that object. I'm guessing that it's because the id is a string, and "string".key won't work. However, if that's the case, is there a way to dynamically get the object names and pass them as arguments? I'm still new, so if I'm not explaining myself well I apologize, hopefully, the code makes more sense.
let peas = {
name : "peas",
attack : 5,
health : 100,
counterAttack : 10,
enemy : false
};
let broccoli = {
name : "broccoli",
attack : 5,
health : 100,
counterAttack : 10,
enemy : false
};
function battleFunction(player, computer) {
//increments playerAttack
newAttack += player.attack;
player.health -= computer.counterAttack;
//deducts attack from computer HP
if (newAttack > player.attack) {
computer.health -= newAttack;
console.log(computer.health);
} else {
computer.health -= player.attack;
console.log(computer.health);
}
if (computer.health <= 0) {
defeatedEnemies++
}
if (defeatedEnemies >= 4) {
alert("You won!");
resetGame();
}
};
$("#fightBtn").click( () => {
battleFunction($("#playerDiv").children("img").attr("id"), $("#computerDiv").children("img").attr("id"));
});
I expect $("#playerDiv").children("img").attr("id") to return 'peas' and it does. Then, I expect player.attack in the function, to work like peas.attack.
If there's just a straight up a better way to do this, I am all ears.
Thank you so much for your help!
Here is an answer to this: 'How do I convert a string to a variable name?'
First, you need a surrounding object. You can use the Window object, but it is not recommended. So, you can see here that I created a simple class that contains two properties that represent your objects "sullivan" is the player and "johnson" is the computer.
Since the Controller class wraps those variable names, then we can use the object created from the class, and use [] bracket notation to gain access to the properties like this:
ctrl[player]
Then if "player" points to the string "sullivan" we can gain access to sullivan's properties.
And you can see that internally to the class, we can access them using the this keyword:
this[player]
I've completed your example below. Let me know if you have questions:
class Controller {
constructor() {
this.newAttack = 0;
this.defeatedEnemies = 0;
this.sullivan = {
name: "Sullivan",
attack: 4,
health: 10
};
this.johnson = {
name: "Johnson",
counterAttack: 8,
health: 10
};
}
battleFunction(player, computer) {
//increments playerAttack
this.newAttack += this[player].attack;
this[player].health -= this[computer].counterAttack;
//deducts attack from computer HP
if (this.newAttack > this[player].attack) {
this[computer].health -= this.newAttack;
console.log(this[computer].health);
} else {
this[computer].health -= this[player].attack;
console.log(this[computer].health);
}
if (this[computer].health <= 0) {
this.defeatedEnemies++
}
if (this.defeatedEnemies >= 4) {
alert("You won!");
//resetGame();
}
};
}
const ctrl = new Controller();
$("#fightBtn").click(() => {
ctrl.battleFunction($("#playerDiv").children("img").attr("id"), $("#computerDiv").children("img").attr("id"));
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button id="fightBtn">Fight!</button>
<div id="playerDiv"><img id="sullivan"></div>
<div id="computerDiv"><img id="johnson"></div>

what is the equivalent of a reduce in javascript

I'm a backend dev moved recently onto js side. I was going through a tutorial and came across the below piece of code.
clickCreate: function(component, event, helper) {
var validExpense = component.find('expenseform').reduce(function (validSoFar, inputCmp) {
// Displays error messages for invalid fields
inputCmp.showHelpMessageIfInvalid();
return validSoFar && inputCmp.get('v.validity').valid;
}, true);
// If we pass error checking, do some real work
if(validExpense){
// Create the new expense
var newExpense = component.get("v.newExpense");
console.log("Create expense: " + JSON.stringify(newExpense));
helper.createExpense(component, newExpense);
}
}
Here I tried to understand a lot on what's happening, there is something called reduce and another thing named validSoFar. I'm unable to understand what's happening under the hood. :-(
I do get the regular loops stuff as done in Java.
Can someone please shower some light on what's happening here. I should be using this a lot in my regular work.
Thanks
The reduce function here is iterating through each input component of the expense form and incrementally mapping to a boolean. If you have say three inputs each with a true validity, the reduce function would return:
true && true where the first true is the initial value passed into reduce.
true && true and where the first true here is the result of the previous result.
true && true
At the end of the reduction, you're left with a single boolean representing the validity of the entire, where by that if just a single input component's validity is false, the entire reduction will amount to false. This is because validSoFar keeps track of the overall validity and is mutated by returning the compound of the whether the form is valid so far and the validity of the current input in iteration.
This is a reasonable equivalent:
var validExpense = true;
var inputCmps = component.find('expenseform')
for (var i = 0; i < inputCmps.length; i++) {
// Displays error messages for invalid fields
inputCmp.showHelpMessageIfInvalid();
if (!inputCmp.get('v.validity').valid) {
validExpense = false;
}
}
// Now we can use validExpense
This is a somewhat strange use of reduce, to be honest, because it does more than simply reducing a list to a single value. It also produces side effects (presumably) in the call to showHelpMessageIfInvalid().
The idea of reduce is simple. Given a list of values that you want to fold down one at a time into a single value (of the same or any other type), you supply a function that takes the current folded value and the next list value and returns a new folded value, and you supply an initial folded value, and reduce combines them by calling the function with each successive list value and the current folded value.
So, for instance,
var items = [
{name: 'foo', price: 7, quantity: 3},
{name: 'bar', price: 5, quantity: 5},
{name: 'baz', price: 19, quantity: 1}
]
const totalPrice = items.reduce(
(total, item) => total + item.price * item.quantity, // folding function
0 // initial value
); //=> 65
It does not make sense to use reduce there and have side effects in the reduce. Better use Array.prototype.filter to get all invalid expense items.
Then use Array.prototype.forEach to produce side effect(s) for each invalid item. You can then check the length of invalid expense items array to see it your input was valid:
function(component, event, helper) {
var invalidExpenses = component.find('expenseform').filter(
function(ex){
//return not valid (!valid)
return !ex.get('v.validity').valid
}
);
invalidExpenses.forEach(
//use forEach if you need a side effect for each thing
function(ex){
ex.showHelpMessageIfInvalid();
}
);
// If we pass error checking, do some real work
if(invalidExpenses.length===0){//no invalid expense items
// Create the new expense
var newExpense = component.get("v.newExpense");
console.log("Create expense: " + JSON.stringify(newExpense));
helper.createExpense(component, newExpense);
}
}
The mdn documentation for Array.prototype.reduce has a good description and examples on how to use it.
It should take an array of things and return one other thing (can be different type of thing). But you won't find any examples there where side effects are initiated in the reducer function.

JavaScript/React Native array(objects) sort

I'm starting with react-native building an app to track lap times from my RC Cars. I have an arduino with TCP connection (server) and for each lap, this arduino sends the current time/lap for all connected clients like this:
{"tx_id":33,"last_time":123456,"lap":612}
In my program (in react-native), I have one state called dados with this struct:
dados[tx_id] = {
tx_id: <tx_id>,
last_time:,
best_lap:0,
best_time:0,
diff:0,
laps:[]
};
This program connects to arduino and when receive some data, just push to this state. More specific in laps array of each transponder. Finally, I get something like this:
dados[33] = {
tx_id:33,
last_time: 456,
best_lap: 3455,
best_time: 32432,
diff: 32,
laps: [{lap:1,time:1234},{lap:2,time:32323},{lap:3,time:3242332}]
}
dados[34] = {
tx_id:34,
last_time: 123,
best_lap: 32234,
best_time: 335343,
diff: 10,
laps: [{lap:1,time:1234},{lap:2,time:32323},{lap:3,time:3242332}]
}
dados[35] = {
tx_id:35,
last_time: 789,
best_lap: 32234,
best_time: 335343,
diff: 8,
laps: [{lap:1,time:1234},{lap:2,time:32323},{lap:3,time:3242332},{lap:4,time:343232}]
}
This data in rendered to View's using map function (not a FlatList).
My problem now is that I need to order this before printing on screen.
Now, with this code, data are printed using tx_id as order, since it's the key for main array. Is there a way to order this array using number of elements in laps property and the second option to sort, use last_time property of element?
In this case, the last tx of my example (35) would be the first in the list because it has one lap more than other elements. The second item would be 34 (because of last_time). And the third would be tx 33.
Is there any way to to this in JavaScript, or I need to create a custom functions and check every item in recursive way?!
Tks #crackhead420
While waiting for reply to this question, I just found what you said.... :)
This is my final teste/solution that worked:
var t_teste = this.state.teste;
t_teste[33] = {tx_id: 33, last_time:998,best_lap:2,best_time:123,diff:0,laps:[{lap:1,time:123},{lap:2,time:456}]};
t_teste[34] = {tx_id: 34, last_time:123,best_lap:2,best_time:123,diff:0,laps:[{lap:1,time:123},{lap:2,time:456}]};
t_teste[35] = {tx_id: 35, last_time:456,best_lap:2,best_time:123,diff:0,laps:[{lap:1,time:123},{lap:2,time:456},{lap:3,time:423}]};
t_teste[36] = {tx_id: 36, last_time:789,best_lap:2,best_time:123,diff:0,laps:[{lap:1,time:123},{lap:2,time:456}]};
console.log('Teste original: ',JSON.stringify(t_teste));
var saida = t_teste.sort(function(a, b) {
if (a.laps.length > b.laps.length) {
return -1;
}
if (a.laps.length < b.laps.length) {
return 1;
}
// In this case, the laps are equal....so let's check last_time
if (a.last_time < b.last_time) {
return -1; // fastest lap (less time) first!
}
if (a.last_time > b.last_time) {
return 1;
}
// Return the same
return 0;
});
console.log('Teste novo: ',JSON.stringify(saida));
Using some simple helper functions, this is definitely possible:
const data = [{tx_id:33,last_time:456,best_lap:3455,best_time:32432,diff:32,laps:[{lap:1,time:1234},{lap:2,time:32323},{lap:3,time:3242332}]},{tx_id:34,last_time:123,best_lap:32234,best_time:335343,diff:10,laps:[{lap:1,time:1234},{lap:2,time:32323},{lap:3,time:3242332}]},{tx_id:35,last_time:789,best_lap:32234,best_time:335343,diff:8,laps:[{lap:1,time:1234},{lap:2,time:32323},{lap:3,time:3242332},{lap:4,time:343232}]}]
const sortBy = fn => (a, b) => -(fn(a) < fn(b)) || +(fn(a) > fn(b))
const sortByLapsLength = sortBy(o => o.laps.length)
const sortByLastTime = sortBy(o => o.last_time)
const sortFn = (a, b) => -sortByLapsLength(a, b) || sortByLastTime(a, b)
data.sort(sortFn)
// show new order of `tx_id`s
console.log(data.map(o => o.tx_id))
sortBy() (more explanation at the link) accepts a function that selects a value as the sorting criteria of a given object. This value must be a string or a number. sortBy() then returns a function that, given two objects, will sort them in ascending order when passed to Array.prototype.sort(). sortFn() uses two of these functions with a logical OR || operator to employ short-circuiting behavior and sort first by laps.length (in descending order, thus the negation -), and then by last_time if two objects' laps.length are equal.
Its possible to sort an object array by theire values:
dados.sort(function(a, b) {
return a.last_time - b.last_time;
});

How to update or add to immutable.js List

I'm using Redux and Immutable.js in my React-based project (built on React Boilerplate) and I'm looking for an idiomatic way to update or add to an Immutable.js List.
My current setup. State initially looks like this:
const initialState = fromJS({
accounts: [],
activeAccount: null,
loadedAccounts: [],
});
I have an Immutable Record for an account object:
const account = new Record({
description: '',
id: '',
name: '',
status: '',
});
And my reducer:
function reducer(state = initialState, action) {
switch (action.type) {
case LOAD_ACCOUNT_SUCCESS:
return state
.set('activeAccount', new account(action.account))
default:
return state;
}
}
This works fine - when a LOAD_ACCOUNT_SUCCESS action is fired, activeAccount is updated to the value of action.account.
I can amend this so that every new LOAD_ACCOUNT_SUCCESS action pushes the newly-loaded account data to loadedAccounts instead:
function reducer(state = initialState, action) {
switch (action.type) {
case LOAD_ACCOUNT_SUCCESS:
const loadedAccount = new account(action.account);
return state
.update('loadedAccounts', (loadedAccounts) => loadedAccounts.push(loadedAccount));
default:
return state;
}
}
However, at the moment loading the same account data twice will result in new Records being pushed to my List each time (duplicating data). What I want to do instead is either add action.account to loadedAccounts (as happens now) or update the Record in the List if there is a matching ID. I'm looking at similar questions and the lamentable Immutable.js documentation and I can't see how to do this: no syntax I've tried works as I expect here.
So, what do you need here is nested update.
At first, you have to check your list of loadedAccounts whether it has this account or not.
Secondly, you have to change activeAccount field.
And, lastly, add (or update) account to loadedAccounts.
The caveat here is how you pass account property. If you derive it from somewhere and pass around as a Record, you can just compare by === (or by .equals()), but it seems that it is just a plain javascript object – I'll suppose it later.
In terms of code it would be something like:
// we can do it by different ways, it is just one of them
const listWithLoadedAccounts = state.get('loadedAccounts');
const isAccountAlready = Boolean(
listWithLoadedAccounts.filter(
account => account.get('id') === action.account.id
).size
);
const patchedState = state.set('activeAccount', action.account.id);
return isAccountAlready
? patchedState.updateIn(['loadedAccounts'], list => list.map(account => account.get('id') === account.action.id ? new account(action.account) : account))
: patchedState.updateIn(['loadedAccounts'], list => list.concat(new account(action.account)))
It is not the ideal code, something can be deduplicated, but you get the idea – always use deep merge / update if you need to change nested fields or data structures.
You also can set new field directly, like:
const oldList = state.get('loadedAccounts');
const newList = oldList.concat(action.account);
const patchedState = state.set('loadedAccounts', newList);
But I personally find that it is not that flexible and also not consistent, because it is quite common operation to perform deep merge.
i hope this example will help, i am creating a new immutable list and first performing an update and then adding a new element. i am not passing the object which i want to replace with, but you can also pass your existing object, Also in update method you have access to current item
class Test {
a = null;
b = null;
constructor(a,b){
this.a=a;
this.b=b;
}
}
$("#test").html("");
function logme(item){
$("#test").append("<br/>"+JSON.stringify(item));
}
function logmeNewLine(){
$("#test").append("<br/>");
}
function listAddUpadte(key){
var index= list.get('data').findIndex(listing => {
return listing.a === key;
});
logme(' found index (-1 for not found) : ' + index);
if(index >= 0){
logme("upadte");
list = list.set("data",list.get("data").update(index,function(item){
return new Test(key,"go");
}));
}else {
logme("add");
list = list.set("data",list.get("data").push(new Test(key,"go")));
}
list.get('data').forEach(item=>{
logme(item);
});
}
var list = Immutable.fromJS({
"data":[new Test(6,"abhi"),new Test(4,"raj"),new Test(1,"ajay")]
});
logme("intial data");
list.get('data').forEach(item=>{
logme(item);
});
logmeNewLine();
logme("testing replace with a = 4 ")
logmeNewLine();
listAddUpadte(4);
logmeNewLine();
logme("testing add with a = 8 ")
logmeNewLine();
listAddUpadte(8);
logmeNewLine();
logmeNewLine();
logmeNewLine();
<script src="https://cdnjs.cloudflare.com/ajax/libs/immutable/3.7.2/immutable.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="test"></div>
Since this is the top search result for what is in the title of the question
https://immutable-js.github.io/immutable-js/docs/#/List/push
An example of how to add to a list using immutable.js:
let oldList = List([ 1, 2, 3, 4 ])
let newList = oldList.push(5)
https://immutable-js.com/docs/v4.0.0/List/#insert()
insert()
Returns a new List with value at index with a size 1 more than this List. Values at indices above index are shifted over by 1.
insert(index: number, value: T): List
Discussion
This is synonymous with list.splice(index, 0, value).
List([ 0, 1, 2, 3, 4 ]).insert(6, 5)
// List [ 0, 1, 2, 3, 4, 5 ]

Categories

Resources