How to transform a js array to make it by day - javascript

const arr = [
{"datetime":"2018/8/5","value":85,"type":"A"},
{"datetime":"2018/8/10","value":7,"type":"B"},
{"datetime":"2018/8/10","value":73,"type":"A"}
];
I have an array as you can see in the snippet. My issue is I need to check something per day:
For each day when A value > 60 or when B value > 6 then do something.
Else when A value <= 60 and when B value <= 6 then do something else.
And I don't know how to do this check with the current array structure as each step in the loop is a different day. I want to compare all values for one day at the same time.
Is it possible to transform the array to look like this? Then I will be able to compare day per day...
const arr = [
{"datetime":"2018/8/5","valueA":85,"valueB":undefined},
{"datetime":"2018/8/10","valueB":7,"valueA":73}
];
Thank you!

You can make a the date groups by reducing into an object. Then just set the appropriate value in that object. In the end your array will be in the Object.keys() of the grouped object.
[As you might surmise from the comments, the order of the final array is not guaranteed because object keys and values are not guaranteed. If your original data is ordered by date, you should say so in the question because there will be more efficient ways to do this if the order is guaranteed].
const arr = [{"datetime":"2018/8/5","value":85,"type":"A"},{"datetime":"2018/8/10","value":7,"type":"B"},{"datetime":"2018/8/10","value":73,"type":"A"}];
let groups = arr.reduce((obj, {datetime, value, type}) => {
if (!obj[datetime]) obj[datetime] = {datetime, valueA:undefined, valueB:undefined}
let currentKey = type == 'A' ? "valueA" : "valueB"
obj[datetime][currentKey] = value
return obj
},{})
let newArr = Object.values(groups)
console.log(newArr)

This will transform the array as OP asked for, and will respect the order.
const arr = [{"datetime":"2018/8/5","value":85,"type":"A"},
{"datetime":"2018/8/10","value":7,"type":"B"},
{"datetime":"2018/8/10","value":73,"type":"A"}];
var daysArr = []
arr.map(function(day){
var keyName = 'value'+day.type
var found = false
var dayObj = {}
for (var i=0; i < daysArr.length; i++) {
if (daysArr[i].datetime === day.datetime) {
daysArr[i][keyName] = day.value;
found = true
break
}
}
if (!found) {
dayObj = {"datetime":day.datetime,valueA:undefined,valueB:undefined}
dayObj[keyName] = day.value
daysArr.push(dayObj)
}
})
console.log(daysArr);

One solution could be using reduce(). Note that if a key is not defined will return undefined (this is exemplified on the second log to the console), so I consider redundant to define, for example "value-B": undefined, unless you want to assign to it another default value.
Warning: As discussed on the comments, you should note that the order of the final result, may not be preserved.
const arr = [
{"datetime":"2018/8/5","value":85,"type":"A"},
{"datetime":"2018/8/10","value":7,"type":"B"},
{"datetime":"2018/8/10","value":73,"type":"A"}
];
let res = arr.reduce((acc, {datetime, value, type: type}) =>
{
acc[datetime] = acc[datetime] || {};
Object.assign(acc[datetime], {datetime, [`value-${type}`]: value});
return acc;
}, {});
console.log(Object.values(res));
console.log(Object.values(res)[0]["value-B"]);

You could do this:
<html>
<head>
<meta charset="UTF-8"></meta>
<script type="text/javascript">
const arr = [{"datetime":"2018/8/5","value":85,"type":"A"},
{"datetime":"2018/8/10","value":7,"type":"B"},
{"datetime":"2018/8/10","value":73,"type":"A"}];
var new_arr = group_items(arr)
console.log(new_arr)
function group_items(arr)
{
var ret_arr = []
for(var x=0;x<arr.length;x++)
{
var cur_date = arr[x].datetime
var pos = lookup_date(cur_date, ret_arr)
var obj = {}
obj.datetime = cur_date
if(pos != false)
{
//add to existing item
if(arr[x].type == 'A')
{
ret_arr[pos].valueA = arr[x].value
}
else if(arr[x].type == 'B')
{
ret_arr[pos].valueB = arr[x].value
}
}
else{
if(arr[x].type == 'A')
{
obj.valueA = arr[x].value
}
else if(arr[x].type == 'B')
{
obj.valueB = arr[x].value
}
ret_arr.push(obj)
}
}
return ret_arr
}
function lookup_date(date, arr)
{
/*
returns the position in arr of date
*/
var retval = false
for(var x=0;x<arr.length;x++)
{
if(arr[x].datetime == date)
{
retval = x
break
}
}
return retval
}
</script>
</head>
<body>
</body>

If you don't need the final array to include the datetimes in the same order as the original, then you can just make an object that maps datetimes to the corresponding values and then use Object.values to get the final array. This approach does not guarantee order, since objects are unordered data structures:
const arr = [
{"datetime":"2018/8/5","value":85,"type":"A"},
{"datetime":"2018/8/10","value":7,"type":"B"},
{"datetime":"2018/8/10","value":73,"type":"A"}
];
const values_by_date = { };
arr.forEach( ({ datetime, type, value }) =>
values_by_date[ datetime ] = {
datetime/*, valueA: undefined, valueB: undefined*/,
...values_by_date[ datetime ], [`value${type}`]: value
}
);
const result = Object.values( values_by_date );
console.log( result );
If you need the final array to include the datetimes in the same order as the original array and the original array is already sorted by datetime, you can do it in a single pass like this:
const arr = [
{"datetime":"2018/8/5","value":85,"type":"A"},
{"datetime":"2018/8/10","value":7,"type":"B"},
{"datetime":"2018/8/10","value":73,"type":"A"}
];
const result = arr.reduce( ({ result, datetime: prev }, { datetime, type, value }) => {
if ( datetime !== prev )
result.push( { datetime/*, valueA: undefined, valueB: undefined*/ } );
Object.assign( result[ result.length - 1 ], { [`value${type}`]: value } );
return { result, datetime };
}, { result: [] } ).result;
console.log( result );
Note: In either snippet you can uncomment /*, valueA: undefined, valueB: undefined*/ if you want the resulting objects to include properties for the missing values.

Related

Processing the data into a desired format in javaScript

I am new to JavaScript and want to process the following array -
var a = [
"John-100",
"Mark-120",
"John-50",
"Mark-130"
]
into the following format -
a = {
"John": [100, 50],
"Mark": [120, 130]
}
But have been unable to do so. Any help will be very much appreciated.
Edit - Any other format ideas where the marks of a particular student can be grouped together are also welcome.
Here is one way to achieve what you described:
var a=[
"John-100",
"Mark-120",
"John-50",
"Mark-130"
]
function convertToSpecialObject(input) {
//setup the output as an empty object
const output = {};
// iterate through input array one element at a time
input.forEach(e => {
// split the current element by dividing it into part[0] before the dash
// and part[1] after the dash sign
const parts = e.split(/-/);
// now check the output object if it already contains a key for the part before the dash
if(!output[parts[0]]) {
// in this case, we don't have a key for it previously
// so lets set it up as a key with an empty array
output[parts[0]] = [];
}
// we must have already created a key or there is a key in existence
// so let's just push the part after the dash to the current key
output[parts[0]].push(Number(parts[1]));
});
// work done
return output;
}
const b = convertToSpecialObject(a);
console.log(b);
you can achieve this by using reduce and split method
var a=[
"John-100",
"Mark-120",
"John-50",
"Mark-130"
]
const b = a.reduce((acc, val) => {
const _split = val.split('-');
const name = _split[0]
if(acc && acc[name]) {
acc[name].push(+_split[1])
} else {
acc[name] = [+_split[1]]
}
return acc;
}, {});
console.log(b)
You can achieve it in a very simple way by just using a Array.forEach() method along with the String.split().
Live Demo :
var a = [
"John-100",
"Mark-120",
"John-50",
"Mark-130"
];
const obj = {};
a.forEach(element => {
if (!obj[element.split('-')[0]]) {
obj[element.split('-')[0]] = [];
}
obj[element.split('-')[0]].push(element.split('-')[1])
});
console.log(obj);
With Simple Approach
const input = [
"John-100",
"Mark-120",
"John-50",
"Mark-130"
];
const getCustomObject = (arr) => {
const obj = {};
for (let i = 0; i < arr.length; i++) {
const split = arr[i].split('-'); //spliting with '-'
if (obj[split[0]]) {
//push to existing array
obj[split[0]].push(split[1]);
} else {
obj[split[0]] = []; //initilize array if no member
obj[split[0]].push(split[1]);
}
};
return obj;
}
console.log(getCustomObject(input));
Now numbers are not numerical values, It can be achieved with parseInt or parseFloat
As I suggested, string split, and array reduce - add in an array map and it's a single line of code
let a=["John-100","Mark-120","John-50","Mark-130"];
a=a.map(v=>v.split('-')).reduce((r,[n,m])=>({...r,[n]:[...r[n]||[],+m]}),{});
console.log(JSON.stringify(a));
The only answer with the correct result ... an array of NUMBERS

Check whether an Object contains a specific value in any of its array

How to find if an object has a value?
My Object looks like below: I have to loop through the object array and check if an array has "SPOUSE" in its value. if exist set a flag spouseExits = true and store the number (in this case (4 because [SPOUSE<NUMBER>] NUMBER is 4) in a variable 'spouseIndex'
This function needs to render in IE9 as well.
eligibilityMap = {
"CHIP": [
"CHILD5"
],
"APTC/CSR": [
"SELF1",
"CHILD2",
"CHILD3",
"SPOUSE4"
]
}
Code:
Object.keys(eligibilityMap).reduce(function (acc, key) {
const array1 = eligibilityMap[key];
//console.log('array1', array1);
array1.forEach(element => console.log(element.indexOf('SPOUSE')))
var spouseExist = array1.forEach(function (element) {
//console.log('ex', element.indexOf('SPOUSE') >= 0);
return element.indexOf('SPOUSE') >= 0;
});
//console.log('spouseExist', spouseExist);
return acc;
}, {});
SpouseIndex is undefined. What am I doing wrong?
Here's a simple approach, supports in all browsers including IE:
var spouseExists = false;
var spouseNumber;
for(var key in eligibilityMap)
{
for(var index in eligibilityMap[key])
{
if (eligibilityMap[key][index].indexOf("SPOUSE") > -1)
{
spouseExists = true;
spouseNumber = eligibilityMap[key][index].replace("SPOUSE", '');
break;
}
}
}
console.log(spouseExists, spouseNumber);
Solution 1: do 3 steps
You can use flatMap to get all sub-array into one.
use findIndex combined with startsWith to get exactly the index of SPOUSE
assign 2 variables based on the value of the found index.
const eligibilityMap = { "CHIP": [ "CHILD5" ], "APTC/CSR": ["SELF1","CHILD2","CHILD3","SPOUSE4"]};
let SpouseExits = false, SpouseIndex = 0;
const arrays = Object.values(eligibilityMap).flatMap(r => r);
const index = arrays.findIndex(str => str.startsWith("SPOUSE"));
if(index >= 0){
SpouseExits = true;
SpouseIndex = arrays[index].slice(-1);
}
console.log({SpouseExits, SpouseIndex});
Solution 2: This function renders in IE9 as well
const eligibilityMap = { "CHIP": [ "CHILD5" ], "APTC/CSR": ["SELF1","CHILD2","CHILD3","SPOUSE4"]};
let SpouseExits = false, SpouseIndex = 0;
for(const [key, value] of Object.entries(eligibilityMap))
{
const index = value.findIndex(function(str){ return str.startsWith("SPOUSE")});
if(index >= 0){
SpouseExits = true;
SpouseIndex = value[index].slice(-1);
}
}
console.log({SpouseExits, SpouseIndex});
Your condition element.indexOf('SPOUSE') >= 0 is not matching any value spouse on your array, because your array has no value named spouse SPOUSE it has SPOUSE4 though.
that's why it's returning undefined.
You may use regex instead of direct matching,
Object.keys(eligibilityMap).reduce(function (acc, key) {
const array1 = eligibilityMap[key];
//console.log('array1', array1);
// array1.forEach(element => console.log(element.indexOf('SPOUSE')))
// var spouseExist = -1;
var spouseExist = array1.filter(function (element,index) {
if(element.match(/SPOUSE/g)) return element; //fixes
});
//fixes
console.log('spouseExist',spouseExist.length>0)
if(spouseExist.length>0){
spouseExist.forEach(element => {
console.log('spouseIndex',element[element.length-1])
});
}
return acc;
}, {});
you can get the number from the spouse name directly, or you can access the index number of the matching spouse from inside the filter function using the value of index and do whatever you like.
Hope this matches your requirement.

Convert array of strings into array of objects where each string itself has a separator

I have an array of strings:
["aa-q1-true", "bb-q1-false", "cc-q1-true", "aa-q2-true", "xx-q2-false", "yy-q2-true", "mm-q3-true", "mn-q3-false", "qr-q3-false"]
Where each string has a meaning. For example if we consider the first string i.e., "aa-q1-true"
Here the first part - aa is the quiz answer, middle part - q1 is the question and true, the last part is the answer status. Status can be true or false. This rule applies for each of the string inside the array.
Now I want to convert it into an array of objects like the following -
[
0: [{
quizAns: [{aa: true}, {bb: false}, {cc: true}]
quizQuestion: q1
}]
1: [{
quizAns: [{aa: true}, {xx: false}, {yy: true}]
quizQuestion: q2
}]
2: [{
quizAns: [{mm: true}, {mn: false}, {qr: false}]
quizQuestion: q3
}]
]
I just could not able to apprehend the logic to do this on my own. If you can just gimme some ideas or solution, it would be very helpful. Thank you so much for your time.
You would want to split each item by -, and then you get the first and last item to get your answer object, and the middle to get which question it belongs to, then you just iteratively construct the solution:
let ans = ["aa-q1-true", "bb-q1-false", "cc-q1-true", "aa-q2-true", "xx-q2-false", "yy-q2-true", "mm-q3-true", "mn-q3-false", "qr-q3-false"].map((s) => s.split('-')).reduce((carry, current) => {
let existingIndex = carry.findIndex((item) => item.quizQuestion === current[1]);
if (existingIndex === -1) {
carry.push({quizAns: [], quizQuestion: current[1]});
existingIndex = carry.length - 1;
}
carry[existingIndex].quizAns.push({[current[0]]: current[2]});
return carry;
}, []);
console.log(ans);
Just another approach with less code
const resultMap = array.reduce((acc, item) => {
// current data
const [answer, quizQuestion, status] = item.split("-");
// previous answers
const previousAnswers = acc[quizQuestion] ? acc[quizQuestion].quizAns : [];
// new answers
const newAnswers = [...previousAnswers, { [answer]: status }];
return { ...acc, [quizQuestion]: { quizQuestion, quizAns: newAnswers } };
}, {});
const result = Object.values(resultMap)
console.log(result)
Just an another approach. Has less looping over the array than the other answer.
let questions = { };
let ans = ["aa-q1-true", "bb-q1-false", "cc-q1-true", "aa-q2-true", "xx-q2-false", "yy-q2-true", "mm-q3-true", "mn-q3-false", "qr-q3-false"]
.reduce((acc, curr) => {
let elements = curr.split("-");
let obj = {};
obj[elements[0]] = elements[2];
if (questions[elements[1]]) {
questions[elements[1]].push(obj);
} else {
questions[elements[1]]= [obj];
}
}, {})
let result = [];
for (let prop in questions) {
result.push({ "quizAns": questions[prop], "quizQuestion": prop });
}
console.log(result);
I am not a big fan of array.reduce syntax cause of readability factor.
This loop will work, even if the input comes in a scrambled fashion.
const a = ["aa-q1-true", "bb-q1-false", "cc-q1-true", "aa-q2-true", "xx-q2-false", "yy-q2-true", "mm-q3-true", "mn-q3-false", "qr-q3-false"];let results = [];
const retObj = (que, ans) => {
const obj = {};
obj[`${que}`] = ans;
return obj;
};
for (let i in a){
let currEleArr = a[i].split("-");
let tarInd = parseInt(currEleArr[1].split("")[1]) - 1;
let que = currEleArr[0];
let ans= currEleArr[2];
if (!results[tarInd])
results[tarInd] = [{quizAns: []}];
if(!results[tarInd][0].quizQuestion)
results[tarInd][0]["quizQuestion"] = `q${tarInd + 1}`;
results[tarInd][0].quizAns.push(retObj(que, ans));
}
console.log(results);

Cleaning the json object by removing duplicates and null and merging them into a single record

Cleaning the JSON object by removing duplicates and null and merging them into a single record
The json array looks like this:
var result =
[
{"id":"10035","occupation":null,"state":"FL"},
{"id":"10035","occupation":"doctor","state":null},
{"id":"10035","occupation":null,"state":null},
]
I want to merge records into one neglecting all the null fields and make it as a single record.Below is my expected output:
[
{"id":"10035","occupation":"doctor","state":"FL"}
]
You could do it with this ES6 script:
let data = [
{"id":"10035","occupation":null,"state":"FL"},
{"id":"10035","occupation":"doctor","state":null},
{"id":"10035","occupation":null,"state":null},
];
let result = Object.values(data.reduce ( (acc, {id, occupation, state}) => {
acc[id] = Object.assign({ id }, acc[id],
occupation && { occupation },
state && { state });
return acc;
}, {}));
console.log(result);
It will still produce multiple records if you have different id values in your input. When there are more than one non-null values for the other properties, but for the same id, then only the last one will survive.
When you're without support for Object.values
Use this definition of it:
Object.values = Object.values || (o => Object.keys(o).map(k => o[k]));
var final = {};
for (var i in result) {
for (var k in result[i]) {
if (result[i][k] && final[k] !== result[i][k]) {
final[k] = result[i][k];
}
}
}
console.log(final); // outputs: {id: "10035", state: "FL", occupation: "doctor"}
Here's a simple to understand example, which works for objects with any number of properties.
let data = [
{"id":"10035","occupation":null,"state":"FL"},
{"id":"10035","occupation":"doctor","state":null},
{"id":"10035","occupation":null,"state":null},
];
let result = data[0];
data.forEach(obj=> { // iterate through all objects in array
for(key in obj) // iterate through all properties of objects
if(obj[key]) result[key] = obj[key]; // if not null, assign to final result
});
console.log(result);
Here is a way to do it in O(n) time:
const mergeObjects = (data) => {
const dataWithoutDuplicates = {};
// first pass will get rid of dupes
let user;
for(let i = 0; i < data.length; data++) {
user = data[i];
if(!dataWithoutDuplicates[user.id]) {
dataWithoutDuplicates[user.id] = user
} else {
Object.keys(dataWithoutDuplicates[user.id]).forEach(key => {
if(dataWithoutDuplicates[user.id][key] === null && user[key]) {
dataWithoutDuplicates[user.id][key] = user[key]
}
})
}
return Object.values(dataWithoutDuplicates)
}

Counting array elements with specific date in javascript

I have an array of Date() objects in javascript and I want to count the number of events on each day.
Here is an example:
What I have is:
Array [ Date 2014-12-04T10:30:20.000Z, Date 2014-12-05T11:04:58.056Z, Date 2014-12-05T11:04:58.056Z, Date 2014-12-05T11:04:58.056Z ]
What I want is:
Array [{date: '2014-12-04', counts: 1}, {date: '2014-12-05', counts: 3}]
Thanks a lot!
Max
Basic answer:
var arr = [], // fill it with array with your data
results = {}, rarr = [], i, date;
for (i=0; i<arr.length; i++) {
// get the date
date = [arr[i].getFullYear(),arr[i].getMonth(),arr[i].getDate()].join("-");
results[date] = results[date] || 0;
results[date]++;
}
// you can always convert it into an array of objects, if you must
for (i in results) {
if (results.hasOwnProperty(i)) {
rarr.push({date:i,counts:results[i]});
}
}
These can be made much easier with lodash functions, and Array.forEach() in ES5
You much better off having a simple object with the keys as the date and the value as the count. I've added a simple pad function that prefixes a zero where the number is a single digit as per your output requirements.
function pad(n) {
return n.toString().length == 1 ? '0' + n : n;
}
function getCount(arr) {
var obj = {};
for (var i = 0, l = arr.length; i < l; i++) {
var thisDate = arr[i];
var day = pad(thisDate.getDate());
var month = pad(thisDate.getMonth() + 1);
var year = thisDate.getFullYear();
var key = [year, day, month].join('-');
obj[key] = obj[key] || 0;
obj[key]++;
}
return obj;
}
getCount(arr); // Object { 2014-04-12: 1, 2014-05-12: 3 }
DEMO
I came across the same issue and found this solution which uses Map()
`
calc = (obj) => {
const orders = []
const dates_map = new Map()
//iterate through all the objects inside the orders array
orders.forEach(order => {
// format and get the date
const date = new Date(order.created_at).toLocaleDateString('en-GB')
//check if the date key exists in the Map() and save it in a temp
const temp = dates_map.get(date) || false
// if it does not exist
if (temp) {
// clone the object
const previous = {...temp}
// increase counter
previous.count += 1
dates_map.set(date, previous)
}else{
//create new object to avoid overwriting
const result = {}
result.count = 1
dates_map.set(date, result)
}
})
console.log(dates_map)
}
And this is the output
Output: Map(3) {
'08/05/2021' => { count: 2 },
'09/05/2021' => { count: 1 },
'11/05/2021' => { count: 2,}
}
`

Categories