JavaScript initializing/appending/updating array of arrays - javascript

I'm new to javascript and have been researching for quite a while now, but can't figure out this syntax. When I try to push an array into another array, it pushes the elements individually instead of creating an array of arrays like I want.
What I'm trying to do:
lastTimes as array of arrays:
lastTimes = [[1, 12435235], [2,443531923], [3,4925951]]
if: IDandTime = [5, 5959393]
append IDandTime to lastTimes as an array:
lastTimes = [[1, 12435235], [2,443531923], [3,4925951], [5, 5959393]]
or if ID (IDandTime[0]) already exists, update that array's time within lastTimes:
if IDandTime = [1, 50305240]
update ID 1's time to 50305240:
lastTimes = [[1, 50305240], [2,443531923], [3,4925951], [5, 5959393]]
Would anyone mind helping me out here please? I've tried many combinations of syntaxes and can't get it right, nor have I been successful in figuring out the proper search term to find a preexisting answer. Any suggestions are greatly appreciated.
EDIT:
code:
var lastTimes = [];
var IDandTime = [1, 5935935];
lastTimes.push(IDandTime);
result:
lastTimes = [1, 5935935]
the result I want:
lastTimes = [[1, 5935935]]
EDIT2:
Ok here is the full function I am working with. I have a node.js server with an XBee serial module, and several Arduino temperature sensors with XBee serial modules. I have a handshake mechanism working, but I'm trying to achieve error checking for when nodes drop out, so their stale data is no longer used. I feel like this is really just a problem with basic 2D array syntax though.
// Open a new serial port connection
sp.on("open", function (err) {
if (err) {
return console.log('Error opening port: ', err.message);
}
console.log('open');
var nodeCount = 0;
var nodes = []; // get rid of after debugging
var lastTimes = [];
lastTimes[0] = [0,0]; // initalize as 2D array for later
// Grab data from buffer
sp.on('data', function(data) {
// Initialize time Object
var time = new Date();
// Split incoming data by newline
var buffer0 = data.split('\n');
// New node handshake initiation received
if (buffer0 == "BROADCASTING") {
nodeCount++;
var sendID = nodeCount.toString();
sp.write(sendID);
console.log("Broadcast received. Sending identifier #" + sendID);
nodes.push(nodeCount);
}
// Preconnected node data received
if ((buffer0 != "BROADCASTING") && (nodeCount > 0)) {
var receiveTime = time.getTime();
// [ID, Temp] touple
var nodeData = buffer0[0].split(" ");
console.log("NodeID: " + nodeData[0] + " Temp(F): " + nodeData[1]);
// [ID, Time] touple
var IDandTime = [];
IDandTime.push(nodeData[0]);
IDandTime.push(time.getTime());
console.log("IDandTime: " + IDandTime);
// Check for preexisting node ID
var oldNode = 0;
var nodeIndex = 0;
for (var i = 0; i < lastTimes.length; i++) {
if (lastTimes[i][0] == IDandTime[0]) {
oldNode = 1;
nodeIndex = i;
}
}
// If new node, add new node data to lastTimes (list of [ID, Time] touples)
if (oldNode == 0) {
lastTimes[lastTimes.length] = IDandTime;
console.log("lastTimes: " + lastTimes);
}
// If preexisting node, update preexisting node time
else if (oldNode == 1) {
lastTimes[i][1] = IDandTime[1];
}
}
});
});
error from my last attempt at finding the proper syntax:
lastTimes[i][1] = IDandTime[1];
^
TypeError: Cannot set property '1' of undefined

You could use findIndex to check if first number is in some of elements in array and change that element in original array or push new element to array.
var lastTimes = [[1, 12435235], [2,443531923], [3,4925951]];
function update(val) {
var i = lastTimes.findIndex(function(e) {
return e[0] == val[0];
});
if (i != -1) {
lastTimes[i][1] = val[1];
} else {
lastTimes.push(val);
}
}
update([1, 50305240])
console.log(lastTimes)

Firstly, if data is string, then calling data.split('\n') returns Array (see. String.prototype.split()), so each of your compares if (buffer0 == "BROADCASTING") are evaluated to false. You probably want to compare first line of the data, and first line is in buffer0[0], so write the conditions if (buffer0[0] == ... ).
Then you have error in code
// If preexisting node, update preexisting node time
else if (oldNode == 1) {
lastTimes[i][1] = IDandTime[1];
}
where you are using variable i from loop, but because the loop is not terminated with break keyword, the loop is always going through the whole array and after the loop is finished, variable i is setted to lastTimes.length, which points to non existing index in the array.
I would write the loop like this:
var IDandTime = [];
var nodeIndex = nodeData[0];
IDandTime.push(nodeIndex);
IDandTime.push(time.getTime());
var foundAtIndex;
for (var i = 0, l = lastTimes.length; i < l; i++) {
if (lastTimes[i][0] === nodeIndex) {
foundAtIndex = i;
break;
}
}
if (typeof foundAtIndex === 'undefined') {
lastTimes.push(IDandTime);
} else {
lastTimes[foundAtIndex] = IDandTime;
}

You could use a pure object (without prototype) instead of an array, like this:
var lastTimes = Object.create(null);
And instead of pushing a tuple, you could just set the properties of this object. This way you don't have to handle updating or appending manually, everything just work automatically, like this:
var receiveTime = time.getTime();
// [ID, Temp] touple
var nodeData = buffer0[0].split(" ");
console.log("NodeID: " + nodeData[0] + " Temp(F): " + nodeData[1]);
// [ID, Time] touple
var IDandTime = [];
IDandTime.push(nodeData[0]);
IDandTime.push(time.getTime());
console.log("IDandTime: " + IDandTime);
lastTimes[nodeData[0]] = time.getTime();
To iterate over the values:
Object.keys(lastTimes).forEach(id => {
var value = lastTimes[id];
});
And to lookup a value by id is just:
var value = lastTimes[id];

Related

Issues with JSON formatting for data object in Grafana

Data is not coming in with proper JSON formatting, so I'm having to loop through items in the array to fix the formatting, parsing the changed items and I cannot use the new object(s) when everything is finished because it is no longer in an array. The data is coming in as follows:
data [datapoints: [0..1..]
target: "up{cluster="bluehills_c3260_cluster",component="atr",datacenter="bluehills",hostname="ny-153-177"...}"]
Is there an easier way to convert this using a .map function or some other method to make things cleaner and get the desired result?
I've tried several methods including .replace, .map, and .push. I've also tried JSON.stringify, but nothing else seems to work except what I currently have.
onDataReceived(data) {
var i;
for (i = 0; i < data.length; i++) { // Loop through data array
var txt = data[i].target; // Create the variable to store the data target
var j;
for (j = 0; j <= txt.length; j++) { // Loop through the data target
var newObj = txt.slice(2,j); // Remove "up"
var filteredObj = newObj // Change over to JSON format...
.replace(/=/g,' : ')
.replace(/,/g,', ')
.replace(/{/g,'{ ')
.replace(/cluster/g,'"cluster"')
.replace(/component/g,'"component"')
.replace(/datacenter/g,'"datacenter"')
}
var dataObj = filteredObj.replace(/_"cluster"/gi,'_cluster');
var finalObj = JSON.parse(dataObj);
console.log("finalObj", dataObj);
}
}
What I want is a single array with the proper JSON format for the data (target) coming in.
How about this?
const myReg = /([\w\s]+)=\"([^"]*)\"/g
const str = `data [datapoints: [0..1..] target: "up{cluster="bluehills_c3260_cluster",component="atr",datacenter="bluehills",hostname="ny-153-177"...}"]`;
let matches = null;
const resultsJson = {};
while(matches = myReg.exec(str)){
resultsJson[matches[1]] = matches[2];
}
{ cluster: 'bluehills_c3260_cluster',
component: 'atr',
datacenter: 'bluehills',
hostname: 'ny-153-177' }
Not sure if this is how you want to have the data stored but that part would be pretty easy to customize.
onDataReceived(data){
this.createCosmo(data);
}
createCosmo(data) {
var arr = $.map(data,function(value){
return value.target;
});
var arr2 = $.map(arr,function(value){
var newObj = value.slice(2); // Remove "up"
var filteredObj = newObj // Change over to JSON format
.replace(/=/g,' : ')
.replace(/,/g,', ')
.replace(/{/g,'{ ')
.replace(/cluster/g,'"cluster"')
.replace(/component/g,'"component"')
.replace(/datacenter/g,'"datacenter"')
.replace(/hostname/g,'"hostname"')
.replace(/instance/g,'"instance"')
.replace(/job/g,'"job"')
.replace(/resilience_group/g,'"resilience_group"')
.replace(/_"cluster"/gi,'_cluster')
.replace(/-"cluster"/gi,'-cluster');
var finalObj = JSON.parse(filteredObj); // Parse the Object into JSON
return finalObj;
});
}

How to compare strings in an array: Javascript

I have an array which looks like
var arr = ["a|c", "a|e", "x|z"];
for(var x in arr){
var appsplit = x.split("|");
}
If the first value(ex: a) in the elements matches then it should combine the values
Ex: output
ace
xz
Please advice how this approach can be done.
You are testing everyone's reading comprehension with that riddle.
var pairs = {};
var arr = ["a|c", "a|e", "x|z"];
for(var x in arr)
{
var appsplit = arr[x].split("|");
if(pairs[appsplit[0]] !== "undefined")
{
pairs[appsplit[0]] = pairs[appsplit[0]] + appsplit[1];
}
else
{
pairs[appsplit[0]] = appsplit[1];
}
}
var matches = [];
for(var x in pairs)
{
matches.push(x + pairs[x]);
}
console.log(matches);
We need to map out the arr elements in this object called pairs. The first value in your split would be the key and the second value is appended (or assigned if it's the first match to the key)
You made an error of splitting x, but you are only splitting the index of the element, not the actual value of the element. arr[x] is the actual value, where x specifies the index in the array.
After we've gone through your arr, we can now merge the key with the values. Your output is contained in matches where the key in each pair is prepended to the value of the key's pair.
Some simple code that would to the trick here.
var arr = ["a|c", "a|e", "x|z", "c|b", "z|e", "c|a"];
var resultObj = {};
arr.forEach(function(element, index){
var array = element.split('|');
if(array.length!==2){
console.log("skipping, invalid input data", element);
} else {
var firstLetter = array[0];
var secondLetter = array[1];
if(resultObj[firstLetter]){
resultObj[firstLetter].push(secondLetter);
} else {
resultObj[firstLetter]=[secondLetter];
}
}
});
Object.keys(resultObj).forEach(function(key){
console.log(key + "," + resultObj[key]);
});
You can use .reduce(), Set to not accumulate duplicate values, .some() to check if previous array contains value in current array, .map(), Array.from() and .join() to convert array to string
var arr = ["a|c", "a|e", "x|z"];
var res = arr.reduce(function(a, b) {
var curr = b.split("|");
var set = new Set;
for (let prop of curr) set.add(prop);
if (!a.length) {
a.push(set)
} else {
for (prop of a) {
if (curr.some(function(el) {
return prop.has(el)
})) {
for (el of curr) {
prop.add(el)
}
} else {
for (let prop of curr) set.add(prop);
a.push(set)
}
}
}
return a
}, []).map(function(m) {
return Array.from([...m], function(el) {
return el
}).join("")
});
console.log(res);
I feel like this can be done more elegantly, but I didn't have time to streamline it. :) The below code will do what you want, though:
var aStartArray = **ARRAY_VALUE_HERE**;
var aSplitResultStrings = [];
// step through each element in the array
for (var i = 0, iSALength = aStartArray.length; i < iSALength; i++) {
// split the values for the current array element
var aSplitVal = aStartArray[i].split("|");
var bStringDoesNotExist = true;
// loop through the "result strings" array
for (var j = 0, iSRSLength = aSplitResultStrings.length; j < iSRSLength; j++) {
// if the first letter from the array element = the first letter of the current "result string" . . .
if (aSplitResultStrings[j].charAt(0) === aSplitVal[0]) {
// append the second letter of the array value to the current result string
aSplitResultStrings[j] = aSplitResultStrings[j] + aSplitVal[1];
// indicate that a match has been found and exit the "result string" loop
bStringDoesNotExist = false;
break;
}
}
// if there are no result strings that start with the first letter of the array value . . .
if (bStringDoesNotExist) {
// concatenate the two values in the current array value and add them as a new "result string"
aSplitResultStrings.push(aSplitVal[0] + aSplitVal[1]);
}
}
Using these arrays, the results are:
aStartArray = ["a|c", "a|e", "x|z"] //results in:
aSplitResultStrings = ["ace", "xz"]
aStartArray = ["a|b", "a|c", "a|d", "a|e", "x|y", "x|z"] //results in:
aSplitResultStrings = ["abcde", "xyz"]
aStartArray = ["a|b", "d|e", "d|f", "x|y", "g|h", "g|i", "m|n", "g|j", "a|c", "x|z"] //results in:
aSplitResultStrings = ["abc", "def", "xyz", "ghij", "mn"]
As I said, this could be more elegant (for example, you could probably use Map to make iterating through the "result strings" easier), but this makes the steps pretty clear and should get you going down the right path towards a final solution.

How do I search a string in JavaScript array using jQuery? [duplicate]

This question already has answers here:
How can I access and process nested objects, arrays, or JSON?
(31 answers)
Closed 6 years ago.
I have a JavaScript array:
var j_array = new Array();
j_arry=["class:1","division:a","class:5","class:3","division:b","division:c","division:d","class:10"];
I need to find how many times the class is coming and its array key, so I use:
found = $.inArray('class', j_array); ` But it returns `-1`;
Then I use:
var search = 'class';
$.each([j_array], function(index, value){
$.each(value, function(key, cell){
if (search.indexOf(cell) !== -1)
console.log('found in array '+index, cell);
});
});
But that is also wrong. How do I solve this?
From this array I want to get the following:
Class coming 4 times, at key 0, 2, 3, and 7
I want to make a separate array of class only, that is,
new_array = ["class:1", "class:2", "class:3", "class:10"];
Currently there are four classes in j_array. How can I get the Nth class value
That is, 1st class value ="class:1", 2nd class value="class:5", etc.
You could filter elements which match in a new array and just return the length of this new array
var j_arry = ["class:1","division:a","class:5","class:3","division:b","division:c","division:d","class:10"];
var res = j_arry.filter(x => x.includes("class"));
var key = res.map(x => x.split(":")[1]);
console.log("Class coming " + res.length + " , at key " + key.join(","));
console.log("new array = ", res);
Use Array.prototype.filter to filter out the elements of the array that contains the string class - see demo below:
var j_array =["class:1","division:a","class:5","class:3","division:b","division:c","division:d","class:10"];
var result = j_array.filter(function(e){
return e.indexOf('class')!==-1;
});
console.log(result);
EDIT:
To get the list of indexes too, you can try this:
var j_array =["class:1","division:a","class:5","class:3","division:b","division:c","division:d","class:10"];
var filteredIndices = []
var filtered = j_array.filter(function(e,i){
if(e.indexOf('class')!==-1) {
filteredIndices.push(i);
return true;
} else {
return false;
}
});
console.log(filtered);
console.log(filteredIndices);
// Nth class value
console.log(filtered[2]); // this prints the 3rd one
.as-console-wrapper{top:0;max-height:100%!important;}
Here is the answer to your questions 1 + 2. It is also 'n' proof so answers your part 3 also. This works by old-fashioned hard graft rather than funky functions. The original array entries are split and filtered then if qualifying we store in an associative array (results) using a pointer array (list) to make it easier to give a sorted result and pull the values from the associative array. The max variable is probably not necessary but included for clarity - could have used list.length instead. Note that the list[] array will be sparse (missing steps) so we test each entry before use in the output steps.
var j_array = new Array();
j_arry=["class:1","division:a","class:5","class:3","division:b","division:c","division:d","class:10","class:1"];
var a, result = [], list=[], max = -1
for (var i =0; i < j_arry.length; i = i + 1) {
var a = j_arry[i].split(":")
if ( a[0] === "class") {
var key = "c" + a[1]
if ( !result[key] ) { result[key] = {pos:[]}}
result[key].cnt = result[key].cnt ? result[key].cnt + 1 : 1;
result[key].pos.push(i)
list[parseInt(a[1])] = "c" + a[1]
max = parseInt(a[1]) > max ? a[1] : max;
}
}
// say locations
for (var i = 0; i < max; i = i + 1) {
if (list[i]) {
key = "c" + i
console.log("Class " + i + " occurs at " + result[key].pos.toString() )
}
}
// make new array
var newArray=[]
for (var i = 0; i < max; i = i + 1) {
if (list[i]) {
newArray.push("Class:" + i)
}
}
console.log("New array=" + newArray.toString() )
Results are:
Class 1 occurs at 0,8
Class 3 occurs at 3
Class 5 occurs at 2
New array=Class:1,Class:3,Class:5
Single reduce is sufficient here.
var arr = ["class:1","division:a","class:5","class:3","division:b","division:c","division:d","class:10"],
res = arr.reduce((p,c) => c.includes("class") ? (p.count++, p.keys.push(c.split(":")[1]), p)
: p ,{count:0, keys:[]});
console.log(res);
You can use the filter and map functions to filter your array to have only elements that match the text 'class', and use array index notation to access the nth element in the array. Check the below code snippet I hope it will be of help to you.
The below code snippet uses ES6 arrow syntax.
var arr = ["class:1", "division:a", "class:5", "class:3", "division:b", "division:c", "division:d", "class:10"];
var result = arr.filter(x => x.indexOf('class') !== -1);
var indices = result.map(x => arr.indexOf(x));
console.log(indices);
console.log(result);
var nValue = window.prompt('Enter n value');
console.log(result[nValue]);
If you're using jQuery to support some really old browser that still don't implement the new Array functions, and you don't want to polyfill those because you're already using jQuery, then you can use the jQuery equivalents:
var arr = ["class:1", "division:a", "class:5", "class:3", "division:b", "division:c", "division:d", "class:10"]
var result = $.grep(arr, function (x) { return x.indexOf('class') !== -1 })
var indices = $.map(result, function (x) { return arr.indexOf(x) })
This is the same code as this answer, but using jQuery.
You have to do map first then filter.
var j_array = ["class:1", "division:a", "class:5", "class:3", "division:b", "division:c", "division:d", "class:10"];
var result = j_array.map(function(e, i) {
return e.indexOf('class') > -1 ? '' + i : false;
}).filter(function(e) {
return !!e;
});
console.log(result);

Select Random Item from Array & Remove It, Restart Once Array is Empty

I'm trying to set select a random item from an array. Once selected, it needs to be removed from the array so it does not get selected again. Finally, once the array is emptied, the process needs to restart. I'm trying to do this using sessionStorage because I need to keep track of which random item gets selected.
// Get array from sessionStorage
myArray = JSON.parse(sessionStorage.getItem("array"));
// If array does not exist in sessionStorage, set it
if (myArray === null) {
sessionStorage.setItem("array", JSON.stringify(["apple", "orange", "banana"]));
// If array exists in sessionStorage, use it to get random item and empty it from array
} else {
var randomItem = myArray[Math.floor(Math.random() * myArray.length)];
console.log(randomItem);
console.log(myArray.splice(randomItem, 1));
}
My JSFiddle can be seen here.
Edit: Updated my work here. Eventually the array is cleared out and restarts.
This probably will not run in this sandbox (use of localstore), but I think it should work if you tried it.
// -------------------------------
// see: http://stackoverflow.com/questions/2450954/how-to-randomize-shuffle-a-javascript-array
// -------------------------------
function _shuffle (array) {
for (var i = array.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
}
// -------------------------------
// -------------------------------
// Get the next "random" item.
// -------------------------------
var randomItem = (function(allItems){
var _key = "array";
var _currentItems = [];
try {
_currentItems = JSON.parse(localStorage.getItem(_key) || "[]");
} catch (e) {
_currentItems = [];
}
if (!Array.isArray(_currentItems) || _currentItems.length === 0 ) {
console.log("resetting");
_currentItems = _shuffle(allItems.slice());
}
var _selectedItem = _currentItems.pop();
localStorage.setItem(_key, JSON.stringify(_currentItems));
return _selectedItem;
})(["apple", "orange", "banana"]);
// -------------------------------
console.log(randomItem);
A more bare bones version [ with _shuffle() from above ] might be just:
var nextItem = (function(all){
var _key = "array";
var _current = JSON.parse(localStorage.getItem(_key) || "[]");
if (_current.length === 0) { _current = _shuffle(all.slice()); }
var _selected = _current.pop();
localStorage.setItem(_key, JSON.stringify(_current));
return _selected;
})(["apple", "orange", "banana"]);
I think the problem you are having is caused by the fact that you are passing the value you get from the array the the splice() function when it is actually expecting an index. Checkout the docs page. so what you would do instead is:
// Get array from sessionStorage
myArray = JSON.parse(sessionStorage.getItem("array"));
// If array does not exist in sessionStorage, set it
if (myArray === null) {
sessionStorage.setItem("array", JSON.stringify(["apple", "orange", "banana"]));
// If array exists in sessionStorage, use it to get random item and empty it from array
} else {
//get random index of item to remove
var randomIndex = Math.floor(Math.random() * myArray.length);
//remove the item at that index
myArray.splice(randomIndex, 1); //this returns an array containing the removed item, so you can capture it if you like
}

How to sort tree of folders

I have a plane tree of folders. This tree has the following properties: id, parent_id, name.
This tree I store in a simple array. The problem is that this array is not sorted.
An element of my array is the simple object like this:
var obj = { id: 1, parent_id: null, name: "Folder" }
I want to sort it in such a way to be able to see some thing like this:
Folder1
Sub_folder1
Sub_sub_folder1
Sub_folder2
Sub_sub_folder2
And so one... I don't want to use recursion and I don't know how to do it properly.
Here is some of my tries. I tryid to add an artificial field which will represented the number of each folder in collection, but it doens't work.
var sort = function(list) {
var f_map = {};
var sorting_index = 1;
var tree = angular.copy(list);
for(var i = 0; i < tree.length; i++) {
var node = tree[i];
f_map[ node.id ]= { index: i, children: [] };
if (node.parent_id) {
f_map[ node.parent_id ].children.push( node.id );
};
var idx = 0;
var visited = {};
for(var key in f_map) {
var index = f_map[key].index;
var node = tree[index];
if (!visited[node.id]) {
node.nuid = idx++;
} else {
visited[node.id] = true;
};
if (f_map[key].children.length) {
var children = f_map[key].children;
for(var i = 0; i < children.length; i++) {
var child_id = children[i];
var child_idx = f_map[child_id].index;
var child = tree[child_idx];
child.nuid = idx++;
visited[child.id] = true;
};
};
};
tree.sort(function(left, right) {
return left.nuid - right.nuid;
});
return tree;
};
Since you're representing the parent pointer as a reference to the id of the parent node, I would first change your representation of the folders into an object representation:
var folders = {
1: {parent_id: null, name: "Folder", path: null},
...
};
I've added a path field, so that I can memoize the results of the following recursive function for finding the full path of a folder:
function path(node) {
if (node.path !== null) return node.path;
if (node.parent_id === null) {
node.path = '/' + node.name;
} else {
node.path = path(folders[node.parent_id]) + '/' + node.name;
}
return node.path;
}
Then we can do a Schwartzian transform by first pulling out the field we want to sort on and a reference to the item:
var keys = [];
Object.keys(folders).map(function (key) {
var folder = folders[key];
keys.push({path: path(folder), id: key});
});
then we can sort the keys array:
keys.sort(function (a, b) {
var apath = a.path;
var bpath = b.path;
// probably the best way to compare folder paths..
return apath.localeCompare(bpath);
});
and finally we can produce the folders in sorted order by traversing the keys array:
var sorted_folders = keys.map(function (item) {
return folders[item.id]; // .name; or maybe .path; ??
});
as is sorted_folders will be a list of folder objects, but per the comment, you can easily pull out the needed properties in this step.
First off, recursion is not slow. It is a nice tool to have in your arsenal. It makes solving certain problems much easier.
Here is an algorithm that should solve it.
1. If the graph can be a forest and not a tree
create a new node root
Make all roots in forest point to this root as parent
2. For every node, create an array (stack) of its children, call it c[i].
3. For each vertex v in tree
c[v.parent].push(v)
4. u = root, i = 0
5. print u
6. while c[root] is not empty and u != root
if c[u] is not empty
u = pop(c[u])
i++
print tab i times
print u
if c[u] is empty and u != root
u = u.parent
i--

Categories