Create array from json with exceptions to the value - javascript

let me show you some working code I have here:
Example that works
[
{
"actual_time":"11:00:00",
"length":"30"
},
{
"actual_time":"13:05:00",
"length":"40"
}
]
var extracted_times = dataObj.map(o => o.actual_time.split(':').slice(0, 2).map(Number));
The result would be like this:
[
[11, 0],
[13, 5]
]
So that works well.
My question:
Here is the problem I'm having. But what if I have this:
[
{
"actual_time":"11:00:00",
"length":"30"
},
{
"actual_time":"13:05:00-00:40:00",
"length":"40"
}
]
As you can see, there is a dash in the middle of two times: 13:05:00-00:40:00. How can I make it so that if there is a dash -, then do something like this:
I want the final result to be like this:
[
[11, 0],
{ from: [13, 0], to: [0, 40] }
]
how can I get it to be like that? Thanks!

var extracted_times = dataObj.map(function(obj){
// match all instances of the time string "hh:mm:ss"
var times = obj.actual_time.match(/\d\d:\d\d:\d\d/g)
// do your thing with the numbers
.map(t=>t.split(':').slice(0,2).map(Number));
// if there is only one, then return it
if(times.length == 1)
return times[0];
// if there are two, then return the object
return { from: times[0], to: times[1] };
});

Solution:
You can use a ternary expression along with your map function to iterate and determine if the String contains a (-) hyphen character by using String.prototype.includes.
--- What's a Ternary?
A ternary expression takes an expression and performs differently if true or false. It looks like this:
(expression) ? if_expression_true : if_expression_false;
The base of our ternary expression is whether or not a -(hyphen) exists in the string, so our improved syntax would look something like this:
(string.includes("-")) ? process_with_hyphen : process_without_hyphen
Code Solution:
data.map(({actual_time: s }) => s.includes("-") ?
([from, to] = s.split("-").map(p), {from,to}) :
p(s),
p = s => s.split(':').slice(0, 2).map(Number), from = to = null);
Code Explanation:
We map over our data, supplying 4 things to the map function
our function that's used with map. (Note: we use destructuring to pull actual_time from the object and store it in the variable s) This function returns one of two things.
If the - (hyphen) character is included
Split the String at the hyphen
map over the created array with the normal process
return an Object with from and to properties
The property values are [0] and [1] of the array respectively
If the - (hyphen) character is not included
perform the normal process on the String
return the resulting array
three variables
p : A function that performs our normal processing on a string and returns the Array. This is what you had in your original question.
from and to: variables we set to null. We do this because in a ternary statement you cannot make variable declarations, only variable assignments. There are multiple ways to do this, but I think this is the cleanest.
Code With Comments
//map over data and store "actual_time" in "s"
data.map(({actual_time: s }) =>
// determine if "s" includes a hyphen
s.includes("-") ?
// if it does
// split at hyphen and process the resulting array with map
// we use destructuring to assign the first and second values of the new array
// to "from" and "to"
// return a new object with "from" and "to" properties
// using the from and to variables
([from, to] = s.split("-").map(p), {from,to}) :
// if it does not
// process the String as normal
p(s),
// our declared variables for the function:
p = s => s.split(':').slice(0, 2).map(Number),
from = to = null);
Working Code Snippet:
let dataObj = [{
"actual_time": "11:00:00",
"length": "30"
},
{
"actual_time": "13:05:00-00:40:00",
"length": "40"
}
],
extract = data => data.map(({actual_time: s }) => s.includes("-") ?
([from, to] = s.split("-").map(p), {from,to}) :
p(s),
p = s => s.split(':').slice(0, 2).map(Number), from = to = null);
let extracted_times = extract(dataObj);
console.log(extracted_times);

Just check to see if the actual_time has - in it first. If not, then use your original algorithm; otherwise, split it by -, and apply the algorithm to both split pieces:
const arr = [
{
"actual_time":"11:00:00",
"length":"30"
},
{
"actual_time":"13:05:00-00:40:00",
"length":"40"
}
];
const timeToNum = time => time.split(':').slice(0, 2).map(Number);
const output = arr.map(({ actual_time }) => {
if (!actual_time.includes('-')) {
return timeToNum(actual_time);
}
const [fromTime, toTime] = actual_time.split('-');
return {
from: timeToNum(fromTime),
to: timeToNum(toTime)
};
});
console.log(output);

I suggest you extract the logic you currently have in the map into a named function and write your logic there.
Something in this spirit
function parseTime(timeString) {
if (timeString.indexOf("-") === -1) {
// no dash, return what you currently have
} else {
// dash found, split on dash and format the object
}
}
dataObj.map(parseTime)
The logic used in the if branch can probably be reused to parse from and to in the else branch, so you might want to make it a function as well.
On another note, I would rather return something like this, which should probably be easier to work with.
[
{ from: [11, 0] },
{ from: [13, 0], to: [0, 40] }
]

Related

Dynamically building object in the loop from another array - Javascript

I want to build an object dynamically from array inside another object. I have put together my code below but that code is fetching the data and building static object.
const data = {
"time": {
"bkts": [
{
"timex": "2021-03-01T00:00:00.000Z",
"bkts": [
{
"key": "abc",
"trd1": {
"value": 0.2
},
"trd2": {
"value": 1.2
}
}
]
},
{
"timex": "2021-02-01T00:00:00.000Z",
"bkts": [
{
"key": "xyz",
"trd1": {
"value": 0.2
},
"trd2": {
"value": 1.2
}
}
]
}
]
}
};
let timestamp = "";
let trd1 = "";
let trd2 = "";
let trdArray1 = [];
let trdArray2 = [];
let dataArray = [];
const responseData = data.time;
responseData.bkts.map(function (val) {
timestamp = new Date(val.timex);
val.bkts.map((sub_val) => {
trd1 = sub_val.trd1.value;
trd2 = sub_val.trd2.value;
});
trdArray1.push([timestamp,trd1]);
trdArray2.push([timestamp,trd2]);
const trd1Object = {
id: "trd1",
name: "Trd 1",
data: trdArray1
};
const trd2Object = {
id: "trd2",
name: "Trd 2",
data: trdArray2
};
const trd1ObjectArray = new Array(trd1Object);
const trd2ObjectArray = new Array(trd2Object);
dataArray = [...trd1ObjectArray,...trd2ObjectArray];
});
console.log(dataArray);
The above code works fine but I want it to be dynamic so if there are 10 numbers of items "trd" then i dont need to build my code 10 times.
I think there is many things to focus on here,
The first thing, your code contains many many syntax errors, some missing commas and closing brackets, so run anywhere and track these errors first until you reach to the error that says Uncaught TypeError: trd1Object is not iterable and then let's talk.
The first point:
You cannot spread an object inside an array, only the opposite is possible.
The second point:
You don't need to use the map() method to loop over the array, the map() method is created to return a new updated array from an existing one, and not to loop over an array to just do some actions.
You can use the forEach() method for this porpuse, and it's the same syntax.
The third point:
You have a problem in declaring variables, which is declaring multiple variables in the same line and assign a value for them all.
This just assigns the value to the last variable, and the first ones will be undefined
which means, if you have this line of code:
let x, y, z = ""
then you find that x = undefined and y = undefined and z = "" so you cannot use this syntax here, I know that it's found in some other languages but not in javascript.
The fourth point:
If you need to build a dynamic code, you must put all the variables except the parent array inside the function of the map() or the forEach() method, which means that every time the function will be called, these variables will be created newly just for this new value, and then append the final result of your function to the parent array.
I could help you with more examples of code snippets if I understood exactly what you need to do, and If the code works well.
And a small advice, when working on anything even for training, name your variables with the full meaning of it, and name everything with a descriptive names, because nobody understands what variables means here.

javascript remove text with double inverted comma not working

I have a json object in a table like below
filter: {where: { AND:[{gender: {IN: "Female"}},{age: {LTE: 44}}]}, relativeDateRange: 90}
which I am fetching and need to change, By removing some of the text.
The new json object will look like below
{"filter": {"where": {"gender": {"IN": "Female"}, "age": {"LTE": 54}},"relativeDateRange": 90}}
One way of doing that is to stringify the object and replacing the keyword,
which in my "theory" should work. however by any mean I am not able to replace ('{"AND":') to blank.
The issue is the keyword contains double inverted comma.
below is the code:
s is the json object which contains the wrong json.
var stringified = JSON.stringify(s);
var t1 = stringified.replace("}{",", ").replace("}}]}","}}").replace('{"AND":', '')
var jsonObject = JSON.parse(t1);
console.log("new json:" +jsonObject);
The text which does not have double inverted comma is getting replaced.
Even using regex or escape character is not helping.
Any suggestion
One option you can try is to use the replacer function which is a parameter in JSON.stringify:
// Your initial filter object
const filter = {filter: {where: { AND:[{gender: {IN: "Female"}},{age: {LTE: 44}}]}, relativeDateRange: 90}};
// Define your replacer function:
const replacer = (name, value) => {
if (name === 'where') {
const array = value['AND'];
// Merge array elements or alter your output in any other way
return Object.assign({}, array[0], array[1]);
}
return value;
}
// The resulting output
console.log(JSON.stringify(filter, replacer, 2));
will produce the following output:
{
"filter": {
"where": {
"gender": {
"IN": "Female"
},
"age": {
"LTE": 44
}
},
"relativeDateRange": 90
}
}
I don't think it's the colon that's throwing off your text replacement statement. How about replacing the "AND" string first, before you complicate it by replacing the curley braces? It should not otherwise effect your other replacements.
var t1 = stringified.replace("AND:[","").replace("}{",", ").replace("}}]}","}}");
The final resolution or more precisely the problem i figure out was,
When a josn is stringified, Its add '\' before every word. As for example when below json is stringified.
filter: {where: { AND:[{gender: {IN: "Female"}},{age: {LTE: 44}}]}, relativeDateRange: 90}
it gives the output as
\filter: {\where: { \AND ....
And the simple resolution was to use replace with double backslash as below.
var t1 = stringified.replace('{\\"AND\\":','').replace(/}{/g,",").replace("}}},","}},").replace('{/"/":','')

Postman: How to check the data type of each value in a response

How do I check that the data type of each value in an API response is NOT an integer?
For example, if my API returns this:
"teamPermissions": [
"Edit",
"Administrator",
"ReadOnly",
etc
]
I need a Postman test to make sure that an integer is never returned in the teamPermission array.
Here's what I started but need assistance:
var jsonData = JSON.parse(responseBody);
tests["Team Permissions Do Not Contain Integers"] = typeof(jsonData.teamPermissions) !== "number"
This passes because teamPermissions is an object but how do I check each value of the object is not an integer?
This should do the check for you:
pm.test('Not contain numbers', () => {
var jsonData = pm.response.json()
for (i = 0; i < jsonData.teamPermissions.length; i++) {
pm.expect(jsonData.teamPermissions[i]).to.not.be.a('number')
}
})
Here's what the check will do if a number is part of the array, I've logged out the types so you can see what it's checking against.
Another alternative is to use Lodash, it's a build-in module for the Postman native app. This code will run the same check as the one above:
pm.test('Not contain numbers', () => {
_.each(pm.response.json().teamPermissions, (arrItem) => {
pm.expect(arrItem).to.not.be.a('number')
})
})
It looks like teamPermissions is an Array.
You could use Array.prototype.every() to check if all the elements pass some condition:
var array1 = [1, 2, 3];
var array2 = ["a", 9, 10];
console.log(array1.every((e) => { return !isNaN(e) })) // True, all elements are numbers
console.log(array2.every((e) => { return !isNaN(e) })) // False
I assume you can do something like this:
jsonData.forEach((a)=>{
var boolVal=isNaN(a) ? true : false
if(boolVal)
{
tests["Team Permissions Do Not Contain Integers"]=false;
}
});
FYI , Instead isNaN(a) you can also do Number.isInteger(a)

How to display only the latest data received?

Hey I am trying to show only the latest message of the day,
Thing is I am not sure how to do that as my code only picks up the one I first wrote..
it's not even the first object in the array.. but it still takes it.
*Note that this code runs every few seconds in intervals to check for new data received.
Below is the response I am using my logic on and the code with the logic
isMOTD = false;
var i = 0;
var MOTD = "";
while (messages[i].text && isMOTD == false) {
i++;
isMOTD = messages[i].text.includes("MOTD");
if (isMOTD)
MOTD = messages[i].text;
}
if (isMOTD) {
console.log(MOTD+' ' +'THIS IS MSG OF THE DAY')
$('.content', el).html(MOTD);
}
}
};
I would do something like this:
var MOTD = messages.filter(message => message.text.includes("MOTD"))
.reduce((a, b) => a.ts > b.ts ? a : b, {ts: -1, text: ''})
.text;
$('.content', el).html(MOTD);
The .filter() creates a new array which only includes messages with MOTD in them.
the .reduce() is going through that filtered array and keeping only the message who's timestamp is highest. I also have it default to an empty string if there are no strings that contain MOTD
And then .text, to deal with just the string, not the timestamps.
EDIT: i've been requested to add some more explanation.
First: arrow functions. EcmaScript 2015 (one of the newer versions of javascript) gave a new way to write functions. When you see an =>, think "function". Rather than doing this:
function (a, b) {
return a + b;
}
you can do this:
(a, b) => {
return a + b;
}
Or if there's just one statement as in this case, you can leave off the curly brackets, the semicolon, and the return:
(a, b) => a + b
Second: .filter. All arrays have a function on them called .filter. As the name suggests, the purpose is to filter the array. You pass in a function that describes how you want it to be filtered, and then it creates a new array with just the matching elements.
So consider the following:
var myArray = [1, 2, 3, 15, 18, 200];
var evenNumbers = myArray.filter(function (num) {
return num % 2 == 0
});
It will loop through the array, and for each element of the array, it calls the function i specified. If the function returns true, then that element is included. If the function returns false, then the element is not included. So for this sample code, evenNumbers will equal [2, 18, 200]
For your case, the filter that i'm doing is:
messages.filter(function (message) {
return message.text.includes("MOTD");
});
So the array that's returned by this will contain all messages who's text includes "MOTD". messages that lack "MOTD" are excluded.
Third: .reduce. All arrays have this function as well. Reduce is quite a versatile function, but the general purpose is to take your array, and in some way boil it down (or "reduce" it) to a single value. You pass in some function that you want to be called for every element of the array. When your function returns a value, that value gets carried forward and used the next time the function is run. Here's an example where i want to sum up all the numbers in an array:
var myArray = [1, 2, 3, 4];
var total = myArray.reduce(function (sumSoFar, current) {
var newSum = sumSoFar + current;
return newSum;
}, 0); //<--- this 0 is the initial value
So here's how it works: It's going to start with the initial value of 0, and then it calls the function. SumSoFar will be 0 at this point (because of the intial value), and current is 1 (because we're starting with the first element of the array, which has a value of 1). The function adds them together, and then returns a new sum of 1. Then the function gets called again, but now sumSoFar is 1 (because that's what was returned the last time), and current is 2 (because we're looking at the second element). It adds 1 + 2, and returns a new sum of 3. And so it continues, adding 3 + 3 to get 6, then adding 6 + 4 to get 10. We're done going the the array, so the final value is 10.
In your case, i want to step through the array and find only the message with the most recent timestamp. I start with an initial value of {ts: -1, text: ''}, because in case there are no messages with "MOTD" in them, i want to have the empty string be the result.
.reduce(function (mostRecentMessageSoFar, currentMessage) {
if (mostRecentMessageSoFar.ts > currentMessage.ts) {
return mostRecentMessageSoFar;
} else {
return currentMessage;
}
}, {ts: -1, text: ''});
So this will walk its way through the array (and remember, we're walking through the filtered array, so they all have "MOTD" in them), it is looking for the most recent message. Any time currentMessage is more recent than the best we've found so far, it switches to that one being the best so far. And in the end, we get the most recent message from the entire array.
That final message is an object that looks something like this:
{
type: 'message',
user: 'U0GL3BR52',
text: 'SOLVE MORE CASES #MOTD',
ts: 1505236695.000533
}
Only the text needs to be put into the dom, so i access that with .text
So, in long form, my solution is this:
var MOTDMessages = messages.filter(function (message) {
return message.text.includes("MOTD");
});
var MostRecentMOTDMessage = MOTDMessages.reduce(
function (mostRecentMessageSoFar, currentMessage) {
if (mostRecentMessageSoFar.ts > currentMessage.ts) {
return mostRecentMessageSoFar;
} else {
return currentMessage;
}
}, {ts: -1, text: ''});
var MOTDText = MostRecentMOTDMessage.text;
$('.content', el).html(MOTDText);
Just sort your message after time descending:
messages.sort((a,b) => b.ts - a.ts );
Then just take the first one:
messages[0]

Javascript function and array problem

I am trying to pass some data to a function that uses those arguments as identifiers for a multi dimensional array, and then return the value hardcoded to that array. I am not sure what I am doing wrong, but something is breaking.
I can get an alert() to pop before I assign any array values, but it seems to die at that point. Any help is appreciated.
// Get Column A's number
var a1 = Number($('#p-a').attr("numb"));
// Get Column B's number
var b1 = Number($('#p-b').attr("numb"));
// Get status for column A
var a_status = $('#p-a').attr("status");
// Get status for column A
var b_status = $('#p-b').attr("status");
// If same, status="s" else, status="i"
var status = "";
if(a_status == b_status) { status = "s"; }else{ status = "o"; }
// Get the value of the numbers + status
var a = this_function(a1, b1, status, "2");
// Update the status div
$('#status').html(a);
function this_function(a1, a2, s, p)
{
this_array = array();
this_array['1']['1']['1']['1'] = "10";
this_array['1']['1']['1']['2'] = "20";
this_array['1']['2']['1']['1'] = "40";
this_array['1']['2']['1']['2'] = "60";
//
return this_array[a1][a2][s][p];
}
You cannot initialize arrays like that. Every level needs to be initialized individually. And as you don't have numerical keys only, I'd use an object instead:
var this_array = {
'1': {
'1': {
'o': {
'1': "10",
'2': "20"
}
},
'2': {
'o': {
'1': "40",
'2': "60"
}
}
}
};
You'd also have to define what happens if a key does not exist. E.g. currently, if status is 's' then you will get an error.
The if statement can be written shorter using the conditional operator:
var status = (a_status == b_status) ? 's' : 'o';
Update: If you really want to have a numerical array, provided the keys are numerical only, you can create the array like so:
var this_array = [
[], // this_array[0]
[ // this_array[1]
[], // this_array[1][0]
[ // this_array[1][1]
[], // this_array[1][1][0]
[null, 10, 20] // this_array[1][1][1][...]
],
[ // this_array[1][2]
[], // this_array[1][2][0]
[null, 40, 60] // this_array[1][2][1][...]
]
]
];
You see, if you do not start your indices with 0 the structure becomes quite confusing.
Your array notation within this_function is incorrect (barring your having an array function that creates the array in the form you show). Notes:
function this_function(a1, a2, s, p)
{
this_array = array(); // <== There is no `array` function in std. JavaScript
this_array['1']['1']['o']['1'] = "10"; // <== Unless you've created an object/array at this_array['1'] (which you haven't), this line will fail
this_array['1']['1']['o']['2'] = "20";
this_array['1']['2']['o']['1'] = "40";
this_array['1']['2']['o']['2'] = "60";
//
return this_array[a1][a2][s][p];
}
I'm not entirely sure what this_function should do, or I'd offer a replacement function. Some thoughts:
Creating an array, you use [] (or new Array(), but that's just a longer form of the same thing).
You have to create each object/array in an array. So you can't assign to this_array['1']['1']['o']['1'], for instance, until you've created an object/array at this_array, this_array['1'], this_array['1']['1'], and this_array['1']['1']['o'].
Your this_function function will create a new array each time it's called. That seems dramatically inefficient.
JavaScript arrays aren't really arrays, they're just objects with some special features. You may just want objects, given that not all of your keys are numeric. If you really want arrays, though, they mostly start with index 0 rather than 1.
You're quite correct that array indexes are really strings, but they're almost always written as numbers and it's totally fine to do that (leaving off the quotes). Using the quotes (which is, again, technically correct) will tend to confuse people trying to maintain the code. (But if you use objects rather than arrays, it will mostly help.)
First and foremost there is no array() function in Javascript. I don't know if it refers to some other point of your code but arrays are created via the array constructor new Array() or an array literal []
Secondly what you are using is not a real numberical indexed array.
For the assignment part: you have one array/object but the deeply nested objects/arrays are undefined.
Your code dies at: this_array['1']['1']['o']['1'] = "10"; because this_array['1'] is undefined which can't have any property so the chain is broken.
Then there is a problem with types. You convert the attribute to number by Number($('#p-a').attr("numb")); and then you use strings as indexes. This is related to the array/object confusion.
What you need is to create a real array, and use numerical indexes:
// move it outside so you only
// create this beast once
var array = [ // first level
[ // second level
[ // third level
[10, 20] // fourth level
],
[
[40, 60]
]
// , [...]
]
];
function this_function(a1, a2, s, p) {
return array[a1-1][a2-1][s-1][p-1];
}
i'm not well versed in needing to deal with multidimensional arrays, but you need to define all your inner arrays before you can set them to anything. something like this:
var this_array = [];
this_array['1'] = [];
this_array['1']['1'] = [];
this_array['1']['2'] = [];
this_array['1']['1']['o'] = [];
this_array['1']['2']['o'] = [];
this_array['1']['1']['o']['1'] = "10";
this_array['1']['1']['o']['2'] = "20";
this_array['1']['2']['o']['1'] = "40";
this_array['1']['2']['o']['2'] = "60";
i tried to console this result out and everything came up as undefined, but within the array at least and didn't die.

Categories

Resources