Get values from array in order inside for - javascript

I have this array in react
const pallete = ['#5931B5', '#7842F5', '#2EABE3', '#F2711C']
I want to create a function that will return the first hex, and then the second...
getColor(){
//What code should I have here?
return COLOR
}
This is my main function
updateArray(data){
const dataAux = data;
for(var key in dataAux.datasets) {
if (typeof dataAux.datasets[key].backgroundColor == 'undefined'){
//I want here to take the values from the first array (pallete) and give dataAux.datasets[key]... the first color, then when he reads it again the second one...
for(var i in pallete){
dataAux.datasets[key].backgroundColor = getColor(color????);
}
}
}
}
The const Data have an array of objects something like this:
{labels: [1, 2, 3], datasets: [{label: 'Undefined', pointStyle: 'line', borderColor: pallete[0],borderWidth: 3,yAxisID: "bar-y-axis",data: [1, 2, 3]},{label: 'Undefined',backgroundColor: pallete[1],pointStyle: 'line',borderColor: pallete[1],borderWidth: 3,yAxisID: "bar-y-axis",data: [2, 4, 6]}]}

There is no need to declare the other function to get the color. You can use the following code to get the color and set that as background.
getColor(index){
//What code should I have here?
return pallete[index];
}
updateArray(data){
const dataAux = data;
var i=0;
for(var key in dataAux.datasets) {
if (typeof dataAux.datasets[key].backgroundColor == 'undefined'){
//I want here to take the values from the first array (pallete) and give dataAux.datasets[key]... the first color, then when he reads it again the second one...
dataAux.datasets[key].backgroundColor = getColor(i%4);
i++;
}
}
}

This might be an overkill, but you can create an object with "memory" to provide you with colors.
function ColorProvider(){
var _this=this;
_this.colors = ['#5931B5', '#7842F5', '#2EABE3', '#F2711C'];
_this.currentIndex = 0;
_this.getNext = function() {
let returnedColor = _this.colors[_this.currentIndex];
if(_this.currentIndex == _this.colors.length-1){
_this.currentIndex = 0;
} else {
_this.currentIndex++;
}
}
}
let color = new ColorProvider();
dataAux.datasets[key].backgroundColor = getColor(color.getNext());
Excuse my archaic JavaScript practices, it's just a POC. I'm sure there is a more modern/slick to go around this, the point is having an object for it instead of primitives.

if you want to get each color on each call of function you can leverage the generator function functionality like below to get each color on each iteration of your loop:
const pallete = ['#5931B5', '#7842F5', '#2EABE3', '#F2711C']
function* getColor(arr) {
for (let i = 0; i < arr.length ; i++) {
yield arr[i]
}
}
const iterator = getColor(pallete)
console.log(iterator.next()) // { value: '#5931B5', done: false }
console.log(iterator.next()) // { value: '#7842F5', done: false }
console.log(iterator.next()) // { value: '#2EABE3', done: false }
console.log(iterator.next()) // { value: '#F2711C', done: false }
console.log(iterator.next()) // { value: undefined, done: true }
in your case, you can call the iterator.next() value inside of your loop until it reaches the undefined condition and then you need to break the loop!

Related

why is iterator objects variable undefined?

Learning to make objects iterable in Javascript.
Object is:
var arrayLikeObject = {
0: "hello",
1: "there",
2: "crappy coder",
length: 3,
}
then I do this to make it iterable:
arrayLikeObject[Symbol.iterator] = function(){
return {
current: 0, // <---- but... it IS defined.
next() {
// let current = 0; // putting it here makes it work
if(current < this.length) {
let a = current;
current++;
return {done: false, value: this[a]};
}
else {
return {done: true};
}
}
};
};
then when I run it with:
console.log("after making it iterable: ==============");
for(let str of arrayLikeObject) {
console.log(str);
}
I get "current is not defined" But as far as I can see, it is. I just can not understand. I thought functions could see variables outside their scope, but not the other way around, unless they get "overshadowed" if that's the correct terminology. I forgot.
current is not a variable, it is a property, so you would need to reference it as this.current.
However, you have another issue with this:
In this.length and this[a], the this object is not arrayLikeObject, but the object that has the next() method.
You can also fix this, but I think it is simpler to go the other way, and make next an arrow function. That way this.length and this[a] will work as intended. Make current a normal variable within the closure:
var arrayLikeObject = {
0: "hello",
1: "there",
2: "crappy coder",
length: 3,
}
arrayLikeObject[Symbol.iterator] = function(){
let current = 0;
return {
next: () => {
if(current < this.length) {
return {done: false, value: this[current++]};
}
else {
return {done: true};
}
}
};
};
console.log("after making it iterable: ==============");
for(let str of arrayLikeObject) {
console.log(str);
}

JS: Cannot read property 'sort' of undefined - define parameters of a class function

I'm finishing my Twilioquest Javascript Tutorial and one of the final tasks is to build a class with some functions. Every time I run this I get
TypeError: Cannot read property 'sort' of undefined. (at array.sort();)
class Ducktypium {
constructor(color) {
this.color = color;
if (color != 'red' || 'blue' || 'yellow' ){
throw error = ("Wrong color!");
}
}
calibrationSequence = [];
calibrate(input) {
let array = input;
let calibrationSorted = array.sort();
let calibrationMuliplied = calibrationSorted.forEach(item => {
item * 3;
});
return calibrationMuliplied
}
calibrationSequence = this.calibrate();
}
var dt = new Ducktypium('red');
dt.calibrate([3, 5, 1]);
console.log(dt.calibrationSequence); // prints [3, 9, 15]
The Error is at the calibrate() function of the class. Probably much more errors to it, but I am focusing on the one the console is throwing at me.
I checked similar questions and think that I understand the issue, but their context is different and I just can't get it solved.
As far as I understand array does not receive the value I'm trying to pass via dt.calibrate([3, 5, 15]); so the .sort() function doesn't apply to anything. But the code looks similar to other attempts at doing something like this so I just can't find the problem.
Here is the correct code with detailed explanations
class Ducktypium {
constructor(color) {
this.color = color;
// && means AND while || means OR
// You have to be specific with your conditions and the if statement
// only runs when the condition provided is met
if (color != 'red' && color != 'blue' && color != 'yellow' ) {
throw Error("Wrong color!"); // You throw an Error object
}
}
// calibrationSequence = [];
calibrate(input) {
let array = input;
let calibrationSorted = array.sort();
// This line is wrong. forEach loops through the items in your array and pass that item into a function but doesn't return anything
// let calibrationMuliplied = calibrationSorted.forEach(item => {
// item * 3;
// });
// What you intended was the map() function
// map() calls the function you provide on each item and modifies them in the array
let calibrationMultiplied = calibrationSorted.map((item) => {
return item * 3
});
// The difference between forEach and map is that
// Lets say you have a box of numbered balls
// in forEach you take out each ball and note the number multiplied by two and put the
// ball back
// in map you take out each ball and actually rewrite the number as its number multiplied
// by two and put it back in
return calibrationMultiplied
}
// You are calling the calibrate method without passing anything into "input"
// that's why sort() is called on nothing and you get an error
// calibrationSequence = this.calibrate();
}
You have wrong statement of "calibrationSequence = this.calibrate()", please make it like bellow code and also "if (color != 'red' || 'blue' || 'yellow' )" this should have and condition (this.color !== 'red' && this.color !== 'blue' && this.color !== 'yellow' )
calibrationSequence = [];
calibrate(input) {
let array = input;
let calibrationSorted = array.sort();
let calibrationMuliplied = calibrationSorted.forEach(item => {
item * 3;
});
return calibrationMuliplied
}
calibrationSequence = this.calibrate;
}

How to use recursion in JavaScript for try do function?

I want to make this code prettier with recursion.
findModel = function(oldModel, ...modelStyles) {
let model = oldModel.elements;
let i = 0;
try {
do {
model = model.children.find(child => child.mStyle === modelStyles[i]);
i += 1;
} while (i < modelStyles.length);
return model;
} catch (e) {
return undefined;
}
};
tried this:
findModel = function(oldModel, ...modelStyles) {
let model = oldModel.elements;
let i = 0;
if (i < modelStyles.length) {
model = model.children.find(child => child.mStyle === modelStyles[i]);
i += 1;
return model;
} else {
return undefined;
}
};
but it's still not working well. in the first code I get only the element, in the second one I get also undefined.
What did I wrong?
As amply noted in comments, you are actually never calling the function recursively.
When it comes to "pretty", I would not go for recursion, but for reduce:
var findModel = function(oldModel, ...modelStyles) {
try {
return modelStyles.reduce((model, style) => model.children.find(child => child.mStyle === style), oldModel.elements);
} catch (e) {} // No need to explicitly return undefined. It is the default
};
If you really need recursion, then first realise that your function expects a first argument type that never occurs again. Only the toplevel model has an elements property, so you can only call this function for ... the top level of your hierarchy.
To make it work, you would need another function that takes the model type as it occurs in the children:
var findModel = function(oldModel, ...modelStyles) {
function recur(model, style, ...modelStyles) {
if (style === undefined) return model;
return recur(model.children.find(child => child.mStyle === style), ...modelStyles);
}
// Need to change the type of the first argument:
try {
return recur(oldModel.elements, ...modelStyles);
} catch (e) {}
};
If you would change the code where the function is called initially, you could of course pass mainmodel.elements instead of mainmodel, so that this type difference problem is resolved. If you can make that change, then the recursive function can become:
var findModel = function(model, style, ...modelStyles) {
if (style === undefined) return model;
try {
return recur(model.children.find(child => child.mStyle === style), ...modelStyles);
} catch (e) {}
};
Still, I would prefer the reduce variant.
The point of recursive function is to call themselves into themselves. In your case, you are calling the function once, but the function never call itself so it just go through once. I'm not sure of the context so I can't fix your code but i can give you an example of recursion.
Lets say we have an object with property. Some are string, some are number and some are objects. If you want to retrieve each key of this object you would need recursion, since you don't know how deep the object goes.
let objectToParse = {
id: 10,
title: 'test',
parent: {
id: 5,
title: 'parent',
someKey: 3,
parent: {
id: 1,
title: 'grand-parent',
parent: null,
someOtherkey: 43
}
}
};
function parseParentKey(object) {
let returnedKey = [];
let ObjectKeys = Object.keys(object);
for(let i = 0; i < ObjectKeys.length; i++) {
if(typeof object[ObjectKeys[i]] === "object" && object[ObjectKeys[i]] !== null) {
// we are calling the methode inside itself because
//the current property is an object.
returnedKey = returnedKey.concat(parseParentKey(object[ObjectKeys[i]]));
}
returnedKey.push(ObjectKeys[i]);
}
return returnedKey;
}
console.log(parseParentKey(objectToParse));
I know this does not answer your question but it gives you a hint on how to use recursion properly. If your first code works, I don't see why you would need to change it in the first place.

Get last value inserted into a Set

The MDN documentation for Set says that JavaScript Set objects retain insertion order of elements:
Set objects are collections of values, you can iterate its elements in insertion order.
Is there a way to get the last item inserted into a Set object?
var s = new Set();
s.add("Alpha");
s.add("Zeta");
s.add("Beta");
console.log(getLastItem(s)); // prints "Beta"
Edit
It is possible to implement a Linked Set datastructure container class that has the same interface as Set and has the desired capability. See my answer below.
I was not able to find any method to get last value inserted in set from ECMA 2015 Specification, may be they never intended such a method, but you can do something like:
const a = new Set([1, 2, 3]);
a.add(10);
const lastValue = Array.from(a).pop();
Edit:
on second thought, a space efficient solution might be:
function getLastValue(set){
let value;
for(value of set);
return value;
}
const a = new Set([1, 2, 3]);
a.add(10);
console.log('last value: ', getLastValue(a));
Some ideas:
Consider using an array instead of a set. Extracting the last element of an array is easy, e.g.
array[array.length-1];
array.slice(-1)[0];
array.pop(); // <-- This alters the array
If you really need a set, you can convert it to an array when you want to extract the last item, but that will cost time and space.
Iterate the set manually. This will cost time but not as much space as copying into an array. For example (there are probably more elegant ways to do this)
var set = new Set([1, 2, 3]);
var iter = set.values(), prev, curr;
do {
prev = curr;
curr = iter.next();
} while(!curr.done)
var last = prev.value; // 3
Consider inserting the items in reverse order. Then you only need to get the first item in the set, and that's easier:
set.values().next().value;
Subclass Set to add this new functionality:
class MySet extends Set {
add(value) {
super.add(value);
this.last = value;
}
}
var set = new MySet();
set.add(1); set.add(2); set.add(3);
set.last; // 3
Note this will only detect values added with add. To be more complete, it should also detect the latest value when the set is constructed, and update the value when the last item is removed.
Yes, there is a way to do that, you can simply convert the set to an array and pop of the last item
function getLastItem(_set) {
return [..._set].pop();
}
to get keys/values etc, you can do
return [..._set.entries()].pop(); // the entire entry
return [..._set.keys()].pop(); // the key only
return [..._set.values()].pop(); // the value only
If you don't want to create an array, you'd probably have to iterate and get the last value, like this
var last; s.forEach(k => { last = k }); // last === "Beta"
FIDDLE
Just another approach.
Set.prototype.last = function(){
return new Set().add( [...this].pop() );
}
Set.prototype.lastKey = function(){
return [...this.keys()].pop();
}
Set.prototype.lastValue = function(){
return [...this.values()].pop();
}
var lastSet = s.last(); // "Beta"
var lastKey = s.lastKey(); // "Beta"
var lastValue = s.lastValue(); // "Beta"
I have created a replacement for Set, which re-implements the linked functionality of the set, using an underlying Map.
class LinkedSetLink {
constructor(value) {
this.value = value;
this.prev = this;
this.next = this;
}
insertBefore(item) {
const prev = item.prev = this.prev;
const next = item.next = this;
next.prev = item;
prev.next = item;
}
remove() {
const prev = this.prev;
const next = this.next;
next.prev = prev;
prev.next = next;
}
}
class LinkedSet {
constructor(iterable) {
this._map = new Map();
this._pivot = new LinkedSetLink(/* undefined */);
if (iterable) {
this._addAll(iterable);
}
}
_addAll(iterable) {
for (const item of iterable) {
this.add(item);
}
}
has(item) {
return this._map.has(item);
}
add(item) {
if (!this._map.has(item)) {
const link = new LinkedSetLink(item);
this._pivot.insertBefore(link);
this._map.set(item, link);
}
}
delete(item) {
const link = this._map.get(item);
if (link) {
this._map.delete(item);
link.remove();
}
}
clear() {
this._map.clear();
this._pivot.next = this._pivot.prev = this._pivot;
}
get size() {
return this._map.size;
}
values() {
return this._map.keys();
}
keys() {
return this.values();
}
[Symbol.iterator]() {
return this.values();
}
*entries() {
for (const key of this.values()) {
yield [key, key];
}
}
first() {
return this._pivot.next.value;
}
last() {
return this._pivot.prev.value;
}
}
function test1() {
console.log(Array.from(new LinkedSet(["a", "b", "c"]).entries()));
}
function test2() {
console.log(new LinkedSet(["a", "b", "c"]).last());
}
<button onclick="test1()">test entries</button>
<button onclick="test2()">test last</button>
There is no method for accessing the last item, but you can do it as follows
let setOfNumbers = new Set([10]);
setOfNumbers.add(20).add(2);
let lastValue = [...setOfNumbers].pop()
A simple solution, but O(N):
const getLastValueFromSet = (set) => {
for (var value of set);
return value;
}

passing array to function - dont get updated

Sorry for the question but I am new to JavaScript
i have defined an object
define(['sharedServices/pubsub', 'sharedServices/topics'], function (pubsub,topics) {
'use strict';
function Incident() {
var that = this;
this._dtasks = [];
this.handlePropertyGet = function(enstate, ename) {
if (!this.entityAspect || !this.entityAspect.entityManager || !this.entityAspect.entityManager.isReady) {
enstate = [];
} else {
enstate = this.entityAspect.entityManager.executeQueryLocally(new breeze.EntityQuery(ename).where('IncidentID', 'eq', this.IncidentID));
}
return enstate;
};
Object.defineProperty(this, 'DTasks', {
get: function () {
return this.handlePropertyGet(this._dtasks, "DTasks");
},
set: function (value) { //used only when loading incidents from the server
that.handlePropertySet('DTask', value);
},
enumerable: true
});
}
return {
Incident: Incident
};
});
when I am calling the property DTasks the inner member _dtask is equal to [], even when i enter the get property and i see that the enstate is filled with the objects when the handlePropertyGet is finished and returned to the get scope the _dtasks remains empty, doesn't it supposed to pass as reference?
this._dtasks "points" to an array. If you pass it as a parameter to this.handlePropertyGet, you make enstate refer to the same array.
If you change the array (as in enstate.push("bar")), the change affects this._dtasks too: you are actually changing neither of them, just the array they both point to.
However, the lines
enstate = []
and
enstate = this.entityAspect.entityManager.executeQueryLocally(new breeze.EntityQuery(ename).where('IncidentID', 'eq', this.IncidentID));
don't modify the array you already have. Instead, they create new arrays and change enstate so that it points to them. this._dtasks, however, remains unchanged.
An easy way to fix it would be to change the code inside the getter to
return this.handlePropertyGet("_dtasks", "DTasks");
and handlePropertyGet to
this.handlePropertyGet = function(enstate, ename) {
if (!this.entityAspect || !this.entityAspect.entityManager || !this.entityAspect.entityManager.isReady) {
this[enstate] = [];
} else {
this[enstate] = this.entityAspect.entityManager.executeQueryLocally(new breeze.EntityQuery(ename).where('IncidentID', 'eq', this.IncidentID));
}
return this[enstate];
};
That way, you would be changing the value of this._dtasks directly.
As an alternative, you could achieve the same result by changing enstate = [] to enstate.length = 0 (which clears the array instead of changing the variable. See https://stackoverflow.com/a/1232046/3191224) and enstate = this.entityAspect.[...] to
var newContent = enstate = this.entityAspect.entityManager.executeQueryLocally(new breeze.EntityQuery(ename).where('IncidentID', 'eq', this.IncidentID));
enstate.length = 0;
Array.prototype.push.apply(enstate, newContent);
which clears the array and then pushes all the elements from the other array, effectively replacing the whole content without changing enstate itself.
My guess is that you are trying to do something like this
Javascript
function Incident() {
var reset = false;
this._dtasks = [];
this.handlePropertyGet = function (ename) {
if (reset) {
this._dtasks = [];
} else {
this._dtasks = [1, 2, 3];
}
return this._dtasks;
};
Object.defineProperty(this, 'DTasks', {
get: function () {
return this.handlePropertyGet("DTasks");
},
enumerable: true
});
}
var x = new Incident();
console.log(x.DTasks);
Output
[1, 2, 3]
On jsFiddle
So you can then use this simplified example with the ideas given by #user3191224

Categories

Resources