Replace hardcoded list of numbers with dynamic array length - javascript

I'm trying to work out how to replace the values 1,2 and 3
inside this GUI interface with a range of numbers based on simply how many objects there are in an array, so that it dynamically keeps track of it without me having to hardcore it in everytime the array is updated.
What is preventing me from simply using an array with numbers itself is that each of the numbers is an object inside of this GUI.
The line of code is this.
gui.add(data, 'system', {
"1": 0,
"2": 1,
"3": 2
})
Suppose I am using any random array holding any random information like so:
var arr = [];
for (var i = 0; i<51;i++){
arr.push("element");}
There are 51 elements in this array and what I'm going for is that the above array holding these elements is used to output the numbers "1", "2", "3", "4", "5", "6" (divided by 10 while still having the 1 leftover) with the GUI interface not breaking down on me. The reason I want it to be divided by 10 is because I don't want to have 51 elements clickable but rather 6 because that's more manageable.
What should I do or look into? Thanks. The fiddle with my code is this. FIDDLE
EDIT: It's a relatively difficult question, so I'll try to be more clear. I have the numerical values 1,2,3 hardcoded in this object:
gui.add(data, 'system', {
"1": 0,
"2": 1,
"3": 2
}).name('system #').onChange(function(value) {
updateSets(value);
});
But what I'm trying to achieve is so that that I don't need to write in the "1", "2", "3" but that it looks to an array's length (which if it holds 3 elements) it'll write them in in the object for me. But if an array has 51 elements, I don't want to have 51 numbers under the system object, but that it divides it by 10 so that I have 6. Not 5, because I have the 1 leftover.

You can use Array.prototype.reduce() to create an object having equal amount of properties and values to .length of an array.
Create a plain object. Utilize Object.entries(), for..of loop to set properties, values of object up to variable n.
You can use Array.prototype.slice() to get n through x properties of values from initially created object.
var arr = [];
for (var i = 0; i < 51; i++) {
arr.push("element");
}
var obj = arr.reduce(function(obj, element, index) {
obj[index + 1] = index;
return obj
}, {});
function getObjProps(from, to, obj, res) {
let it = void 0;
if (from) {
it = Object.entries(obj).slice(from, to);
} else {
it = Object.entries(obj);
}
for (let [key, value] of it) {
if (+key <= to) {
res[key] = value;
} else {
break;
}
};
return res
}
var curr = getObjProps(null, 6, obj, {});
console.log(curr);
var next = getObjProps(6, 12, obj, {});
console.log(next);
for (let i = 12; i < arr.length; i += 6) {
console.log(getObjProps(i, i + 6, obj, {}))
}

Related

Check if object has key, if so, add new key: value to said object

I have a loop that goes over an array of data plotData. Out of plotData I build another array called visiblePoints. Then I loop over visiblePoints to build an object thisRow, then I add thisRow to an array outside of the loop, called dataArray:
var dataArray = []
for (i = 0; i < plotData.length; i++) {
// This filters datapoints array into another array
var visiblePoints = filterPoints(plotData[i].datapoints)
// Get the string for the thisRow object key name
var selectedParameter = plotData[i].label
for (k = 0; k < visiblePoints.length; k++) {
// Convert visiblePoint first value from ms to date
var timestamp = new Date(visiblePoints[k][0])
// Form the thisRow object that will go into dataArray
// for each loop of visiblePoints
var thisRow = {}
thisRow["Time"] = msToTime(timestamp)
thisRow[selectedParameter] = visiblePoints[k][1]
dataArray.push(thisRow)
}
}
Let's simplify and say I only have 2 element in sub array visisblePoints for each plotData array, like so (each plotData array can have many visiblePoints but I'm simplifying to just 2 for each):
plotData[0].visiblePoints = [[00:00:01, 1], [00:00:02, 4] ...]
plotData[1].visiblePoints = [[00:00:01, 27], [00:00:02, 31] ...]
plotData looks like this on the console:
The visiblePoints array for the k loop is derived off of plotData.datapoints and looks like:
Where visiblePoints[n][0] is a value in ms (that I convert into seconds) and visiblePoints[n][1] is just a float value.
visiblePoints =
[0, 0.0500466109191]
[100, 0.0548114598135]
[200, 0.0550143573252]
[300, 0.0549408536766]
[400, 0.0546117305419]
[... repeat 300+ times...]
After looping over plotData[i] and visiblePoints[k] I end up with:
dataArray = [
{
"Time": 00:00:01,
"foo": 1
},
{
"Time": 00:00:01,
"bar": 27
},
{
"Time": 00:00:02,
"foo": 4
},
{
"Time": 00:00:02,
"bar": 31
},
]
When I had meant to end up with:
dataArray = [
{
"Time": 00:00:01,
"foo": 1,
"bar": 27
},
{
"Time": 00:00:02,
"foo": 4,
"bar": 31
}
]
I think in loop k I need to go over dataArray, check all objects there to see if there is a Time key that matches thisRow["Time"] = msToTime(timestamp), and if so, add thisRow[selectedParameter] = visiblePoints[k][1] to that, if not, create a new object.
Problems:
I'm not sure how to check for this in JS (I'm more experience at Python, and not that much at that
It seems like I'm doing a heck of a lot of loops. I'm not sure adding yet another one to go over the entire dataArray and check all objects to see if a key exists in one of them is best solution here. A plotData.visiblePoints array can be 500+ long.
[EDIT] Simplified the question. Added picture examples. Added text examples of the k array.
You can build an object keyed to the grouping — in this case the timestamp. This will let you randomly access the item you want without searching the array. When you'r done the Object.values of the object will be an array of your grouped objects:
let plotData = [
{
label: 'foo',
visiblePoints: [[`00:00:01`, 1], [`00:00:02`, 4]]
},
{
label: 'bar',
visiblePoints: [[`00:00:01`, 27], [`00:00:02`, 31]]
}
]
let groups = plotData.reduce((obj, {label, visiblePoints}) => {
visiblePoints.forEach(([time, val]) => {
if(!obj[time]) obj[time] = {Time: time} // if we haven't seen this time make a new object at that key
obj[time][label] = val
})
return obj
}, {})
console.log(Object.values(groups))
You could take a Map or a hash table which keeps the reference to the object with the same time.
var map = new Map,
timestamp,
row;
// later in loop
timestamp = msToTime(new Date(visiblePoints[k][0]));
row = map.get(timestamp);
if (!row) {
map.set(timestamp, row = { Time: timestamp });
}
row[selectedParameter] = visiblePoints[k][1];
// at the end assign the array
dataArray = Array.from(map.values())
You could find if an Object has a specific key by calling hasOwnProperty method.
const obj = {
"bar": [1,2,3],
"foo": "im foo"
};
console.log(obj.hasOwnProperty("bar"))
console.log(obj.hasOwnProperty("bar2"))
And you need to follow this pattern.
var dataArray = []
for (i = 0; i < 1; i++) {
for (k = 0; k < 2; k++) {
thisRow = {};
thisRow["Time"] = k+1 * i+1
thisRow["foo"] = i+1
thisRow["var"] = k+1
dataArray.push(thisRow)
}
}
console.log(dataArray)

Replace array entry with spread syntax in one line of code? [duplicate]

This question already has answers here:
Replace element at specific position in an array without mutating it
(9 answers)
Closed 3 months ago.
I'm replacing an item in a react state array by using the ... spread syntax. This works:
let newImages = [...this.state.images]
newImages[4] = updatedImage
this.setState({images:newImages})
Would it be possible to do this in one line of code? Something like this? (this doesn't work obviously...)
this.setState({images: [...this.state.images, [4]:updatedImage})
use Array.slice
this.setState({
images: [
...this.state.images.slice(0, 4),
updatedImage,
...this.state.images.slice(5),
],
});
Edit from original post: changed the 3 o a 4 in the second parameter of the slice method since the second parameter points to the member of the array that is beyond the last one kept, it now correctly answers the original question.
Once the change array by copy proposal is widely supported (it's at Stage 3, so should be finding its way into JavaScript engines), you'll be able to do this with the new with method:
// Using a Stage 3 proposal, not widely supported yet as of Nov 17 2022
this.setState({images: this.state.images.with(4, updatedImage)});
Until then, Object.assign does the job:
this.setState({images: Object.assign([], this.state.images, {4: updatedImage}));
...but involves a temporary object (the one at the end). Still, just the one temp object... If you do this with slice and spreading out arrays, it involve several more temporary objects (the two arrays from slice, the iterators for them, the result objects created by calling the iterator's next function [inside the ... handle], etc.).
It works because normal JS arrays aren't really arrays1 (this is subject to optimization, of course), they're objects with some special features. Their "indexes" are actually property names meeting certain criteria2. So there, we're spreading out this.state.images into a new array, passing that into Object.assign as the target, and giving Object.assign an object with a property named "4" (yes, it ends up being a string but we're allowed to write it as a number) with the value we want to update.
Live Example:
const a = [0, 1, 2, 3, 4, 5, 6, 7];
const b = Object.assign([], a, {4: "four"});
console.log(b);
If the 4 can be variable, that's fine, you can use a computed property name (new in ES2015):
let n = 4;
this.setState({images: Object.assign([], this.state.images, {[n]: updatedImage}));
Note the [] around n.
Live Example:
const a = [0, 1, 2, 3, 4, 5, 6, 7];
const index = 4;
const b = Object.assign([], a, {[index]: "four"});
console.log(b);
1 Disclosure: That's a post on my anemic little blog.
2 It's the second paragraph after the bullet list:
An integer index is a String-valued property key that is a canonical numeric String (see 7.1.16) and whose numeric value is either +0 or a positive integer ≤ 253-1. An array index is an integer index whose numeric value i is in the range +0 ≤ i < 232-1.
So that Object.assign does the same thing as your create-the-array-then-update-index-4.
You can use map:
const newImages = this.state.images
.map((image, index) => index === 4 ? updatedImage : image)
You can convert the array to objects (the ...array1), replace the item (the [1]:"seven"), then convert it back to an array (Object.values) :
array1 = ["one", "two", "three"];
array2 = Object.values({...array1, [1]:"seven"});
console.log(array1);
console.log(array2);
Here is my self explaning non-one-liner
const wantedIndex = 4;
const oldArray = state.posts; // example
const updated = {
...oldArray[wantedIndex],
read: !oldArray[wantedIndex].read // attributes to change...
}
const before = oldArray.slice(0, wantedIndex);
const after = oldArray.slice(wantedIndex + 1);
const menu = [
...before,
updated,
...after
]
I refer to #Bardia Rastin solution, and I found that the solution has a mistake at the index value (it replaces item at index 3 but not 4).
If you want to replace the item which has index value, index, the answer should be
this.setState({images: [...this.state.images.slice(0, index), updatedImage, ...this.state.images.slice(index + 1)]})
this.state.images.slice(0, index) is a new array has items start from 0 to index - 1 (index is not included)
this.state.images.slice(index) is a new array has items starts from index and afterwards.
To correctly replace item at index 4, answer should be:
this.setState({images: [...this.state.images.slice(0, 4), updatedImage, ...this.state.images.slice(5)]})
first find the index, here I use the image document id docId as illustration:
const index = images.findIndex((prevPhoto)=>prevPhoto.docId === docId)
this.setState({images: [...this.state.images.slice(0,index), updatedImage, ...this.state.images.slice(index+1)]})
I have tried a lot of using the spread operator. I think when you use splice() it changes the main array. So the solution I discovered is to clone the array in new variables and then split it using the spread operator. The example I used.
var cart = [];
function addItem(item) {
let name = item.name;
let price = item.price;
let count = item.count;
let id = item.id;
cart.push({
id,
name,
price,
count,
});
return;
}
function removeItem(id) {
let itemExist = false;
let index = 0;
for (let j = 0; j < cart.length; j++) {
if (cart[j].id === id) { itemExist = true; break; }
index++;
}
if (itemExist) {
cart.splice(index, 1);
}
return;
}
function changeCount(id, newCount) {
let itemExist = false;
let index = 0;
for (let j = 0; j < cart.length; j++) {
console.log("J: ", j)
if (cart[j].id === id) {
itemExist = true;
index = j;
break;
}
}
console.log(index);
if (itemExist) {
let temp1 = [...cart];
let temp2 = [...cart];
let temp3 = [...cart];
cart = [...temp1.splice(0, index),
{
...temp2[index],
count: newCount
},
...temp3.splice(index + 1, cart.length)
];
}
return;
}
addItem({
id: 1,
name: "item 1",
price: 10,
count: 1
});
addItem({
id: 2,
name: "item 2",
price: 11,
count: 1
});
addItem({
id: 3,
name: "item 3",
price: 12,
count: 2
});
addItem({
id: 4,
name: "item 4",
price: 13,
count: 2
});
changeCount(4, 5);
console.log("AFTER CHANGE!");
console.log(cart);

Javascript: function which takes in object, and returns list of all numbers described in object

Write a function that takes in an object like so {1: 4, 2: 10, 5:3} and then return a list of all the numbers described in the object. Each key-value pair describes a number and how many times it should occur in the array.
Example:
{3 : 10,5 : 2}
[3,3,3,3,3,3,3,3,3,3,5,5]
Also account for empty, null, undefined and non-objects in your code that can be passed in
In those cases just return [], the empty list
Here's what I've been able to produce for. I know I have to do a second loop, but I don't understand how to make the numbers appear in the array the number of times described. Here's my progress:
var numObj = {1:4, 2:10, 3:5};
function numDescribed(numObj) {
var numOfNums = [];
for (var x in numObj) {
numOfNums.push(numObj[x]); //this produces an array of [4, 10, 5]
} for (var i = 0; i < numOfNums.length; i++) {
numOfNums.
}
}
var obj = { 3: 10, 5: 2 };
var res = [];
Object.keys(obj).forEach(function(e) {
for (var i = 0; i < obj[e]; i++) {
res.push(e);
}
});
document.write(res);

Multiple input to one output

How can i handle multiple input values to one output value?
function c(input,val){
return input.indexOf(val)>-1;
}
function result(i){
if(c(i,1) && c(i,2) && c(i,3)){
return "alpha";
}else if(c(i,1) && c(i,2) && !c(i,3)){
return "beta";
}else if(c(i,1) && !c(i,2) && c(i,3)){
return "gamma";
}else if(c(i,1) && !c(i,2)){
return "delta";
}else if(c(i,3)){
return "theta";
}
//..... and so on covering all possible combinations
return null;
}
result([1,2,3]); //output : alpha
result([1,3,2]); //output : alpha
result([1,3,1,1]); //output : gamma
result([1,2,4]); //output : beta
result([3]); //output : theta
Number of values in array can be N, but from a predefined set of values only
What is the right way to work with so many combinations?
Based on Condor's answer, the following should do the job. It uses a sequence of tests to work out the result and also implements the "not" tests. If a value is "true" it must appear, if a value is "false" it must not appear. If it isn't mentioned, it doesn't matter if it's in the values or not.
Duplicates aren't an issue, values are processed until one fails. Removing duplicates first might speed it up a bit though.
The result is the name of the first set of tests that pass.
function testValues(values) {
var checks = [
{alpha: {1:true, 2:true, 3:true }},
{beta : {1:true, 2:true, 3:false}},
{gamma: {1:true, 2:false, 3:true }},
{theta: {3:true}}
];
var check, resultName, tests, passed;
// Do checks in sequence
for (var i=0, iLen=checks.length; i<iLen; i++) {
check = checks[i]
// Get name of result to return if the checks pass
for (resultName in check) {
// Make sure result is own property
if (check.hasOwnProperty(resultName)) {
// Passed is true until a fail is found
passed = true;
// Get tests to perform
tests = check[resultName];
// For each value in tests, make sure value exists or doesn't in values
for (var v in tests) {
if (tests.hasOwnProperty(v)) {
// Only test if passed is true
if (passed) {
// Note that indexOf uses === so must have same type
// Property names are always strings so if passing numbers,
// Must convert to numbers
passed = tests[v] === (values.indexOf(+v) != -1);
}
}
}
// If passed tests, return
if (passed) return resultName;
}
}
}
return 'failed all tests...';
}
console.log(testValues([1,2,3])); //output : alpha
console.log(testValues([1,3,2])); //output : alpha
console.log(testValues([1,3,1,1])); //output : gamma
console.log(testValues([1,2,4])); //output : beta
console.log(testValues([3])); //output : theta
The code could be a bit shorter if Object.keys is used with forEach, but the above is a bit clearer (perhaps), it can be refactored to be shorter. The above will work in ECMA-262 ed 3 environments if a shim for Array.prototype.indexOf is provided.
Edit
If modern features are used, the code can be simplified a bit. Providing support for older browsers isn't difficult:
// These are sufficient to support the function but are not suitable for general use
// Better versions are avaialble at MDN, see links below
if (!Object.keys) {
Object.keys = function(obj) {
var keys = [];
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
keys.push(key);
}
}
return keys;
};
}
if (!Array.prototype.indeOf) {
Array.prototype.indexOf = function (value) {
for (var i=0, iLen=this.length; i<iLen; i++) {
if (this[i] === value) return i;
}
return -1;
};
}
function testValues(values) {
var checks = [
{alpha: {1:true, 2:true, 3:true }},
{beta : {1:true, 2:true, 3:false}},
{gamma: {1:true, 2:false, 3:true }},
{theta: {3:true}}
];
var check, passed, resultName, tests, testKeys;
for (var i=0, iLen=checks.length; i<iLen; i++) {
check = checks[i]
resultName = Object.keys(check)[0];
passed = true;
tests = check[resultName];
testKeys = Object.keys(tests);
for (var j=0, jLen=testKeys.length; j<jLen && passed; j++) {
passed = tests[testKeys[j]] === (values.indexOf(+testKeys[j]) != -1);
}
if (passed) return resultName;
}
return 'failed all tests...';
}
Object.keys: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
Array.prototype.indexOf: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf
The code could perhaps be organized more clearly if the cases are easier accessible. For instance, the alpha case could be formualted by an array [1,2,3] which would list the numbers on which the code depend and and array of boolean values [true,true,true] which would indicate whether the corresponding number occurs or not. The checking could be implemented with a function as follows.
function check_case(values,bools){
var Result = true;
for ( var i = 0; i < values.length; i++ ){
Result = Result && ( c(values[i]) == bools[i] );
}
return Result;
}
This circumvents the formulation of conditions in which results of c have to be negated individually, which makes them difficult to follow and edit.
The idea could be taken a step further by having an array of cases which would also hold the names of the cases. An array containing the first two cases above would then be as follows.
[
{
values: [1,2,3],
bools: [true,true,true],
name: 'alpha'
},
{
values: [1,2,3],
bools: [true,true,false],
name: 'beta'
}
]
The code would then iterate over this array, calling check_case for every index and returning the value of name for the first hit.
Let your predefined array be:- [3,2,1] (i am keeping this short for simplicity, this can extend to N elements)
predefined array as boolean string can be visualized as:- 123
You can treat this number as boolean and create your desired output mapping:-
Example:- If no number :- 000 0
If only 1 is present:- 100 4
If only 1 and 2 are present :- 110 6
and so on all the required boolean combinations can be defined
So for N numbers you can create a list of all the possible combinations you want
Example:- var definedSet = {"0":"Alpha","1":"beta","2":"gama","3":"x","4":"y","5":"z","6":"a","7":"b"};
Now take input and remove duplicates and check the location (bit location of the number and create a boolean value and map this to defined set)
Code:-
function sortAndRemoveDuplicates(arr) {
arr.sort( function(a, b) { return a - b; } );
var copy = arr.slice(0);
arr.length = 0;
for (var i = 0, len = copy.length; i < len; ++i) {
if (i == 0 || copy[i] != copy[i - 1]) {
arr.push(copy[i]);
}
}
return arr;
}
var definedArr = [3,2,1];
var definedSet = {"0":"Alpha","1":"beta","2":"gama","3":"x","4":"y","5":"z","6":"a","7":"b"};
var inputArr=[1,4,3,1,1];
sortAndRemoveDuplicates(inputArr);
var outBooleanValue = 0;
for(var i=0;i<inputArr.length;i++)
{
var numIndex =definedArr.indexOf(inputArr[i]);
if(numIndex!=-1)
{
outBooleanValue+=Math.pow(2,numIndex);
}
}
result =definedSet[outBooleanValue.toString()];
alert(result);
Here is the working fiddle:-
http://jsfiddle.net/9tFnn/1/
Here is another version of this where predefined inputs are not required and it allows you to give conditions as you specified:-
var definedSet = {"1":"alpha","12":"beta","12!3":"gama","123":"delta"};
$("#defined-set").html(JSON.stringify(definedSet));
var inputArr=[1,2];
$("#input").html(JSON.stringify(inputArr));
$.unique(inputArr);
inputArr.sort();
var outputStr = inputArr.join('');
var regexStr = '';
var loopCounter=0;
for(loopCounter=0;loopCounter<outputStr.length;loopCounter++)
regexStr+='(!\\d)*'+outputStr[loopCounter]+'(!\\d)*';
regexStr+='$';
//var regexPattern = new RegExp(regexStr);
console.log(regexStr);
for(var currSet in definedSet)
{
//var match = regexPattern.test(currSet);
var match = currSet.search(regexStr);
if(match!=-1)
{
if(currSet==outputStr)
{
$("#result").append("Exact Match::");
}
$("#result").append(currSet+"<br/>");
}
}
Here is the link to working fiddle:-
http://jsfiddle.net/hLUuF/1/
Note:- This is just a basic prototype of the algorithm used to get to the answer, this can be modified according to the programming needs.
Hope this proves to be useful.
This solution assumes that the order of elements in the input is not important. That is, an input of [1, 3, 2] is the same as an input of [1, 2, 3].
It looks like what you're trying to do is take a set of possible inputs from a range (1..n), each of which may or may not be present. In other words, for a range of [1, 2, 3, 4], one possible input would be [1, 3]. Or,
Out of: [1, 2, 3, 4]
We want: [x, 0, x, 0]
Which looks suspiciously like a binary number, especially if we reverse the order. So all we really need to do is
Convert our input to a binary number
Use that binary number as an index into a predefined look-up table of values
For the first part all we need to do is loop through the input values and OR the appropriate bits together. This also automatically takes care of any duplicates.
for (i=0;i<input_array.length;i++)
{
num = num | (1 << (input_array[i] - 1));
}
The - 1 in there is because our sequence starts from 1 rather than 0. So an input of 4 will result in 1000 and an input of 2 will result in 0010. OR-ing them together gives us 1010 or 10 (decimal).
Now num contains the index into our table which we set up beforehand:
values[0] = "alpha"; // Corresponding to input []
values[1] = "beta"; // Corresponding to input [1]
...
values[10] = "gamma"; // Corresponding to input [4, 2] - remember, it's reversed
...
values[15] = "theta"; // Corresponding to input [4, 3, 2, 1]

Summate value of matching property in an array in Javascript

In C#, we have a .Sum(Predicate) method that lets you use a lambda expression to do a summation over a collection of objects. For instance, if I had the following set of objects (using JSON for simplicity)
[
{
"Id": 1,
"Sub": {
"Size": 1
}
},
{
"Id": 2,
"Sub": {
"Size": 3
}
}
]
And I wanted to get the sum of the sizes, I could do Sum(n => n.Sub.Size)
Is there a way to do something like this in javascript? I am really new (and weak) at the language and am having trouble performing a similar function. I am using jQuery, so I am open to that opening anything too.
var result = arr.reduce(function(a, b) {
return a + b.Sub.Size;
}, 0);
Demo: http://jsfiddle.net/sUCTK/
Documentation: MDN
In JavaScript you'll need to manually step through the array and add up the values:
var sum = 0;
for (var i in a) {
sum += a[i].Sub.Size
}
// sum now contains the total
For cross-browser compatibility, you could do this:
var sum = function(arr, callback, initial_value) {
if (arguments.length < 3) {
initial_value = 0;
}
var ret = initial_value;
for (var i = 0; i < arr.length; i++) {
ret += callback(arr[i]);
}
return ret;
};
console.log(sum(arr, function(element) {
return element.Sub.Size;
});
This would also let you sum other summable objects, like strings, by passing the appropriate-type initial_value.
#zerkms shows how to use new ECMAScript standards to better sum things.

Categories