Related
I have a nested json array and I am trying to get the maximum value of the points attribute in this array.
data = {
"name": "KSE100",
"children": [
{
"name": "TECHNOLOGY & COMMUNICATION",
"children": [
{
"name": "TRG",
'points': -21
},
{
"name": "SYS",
},
]
},
{
"name": "OIL",
"children": [
{
"name": "PPL",
'points': 9
},
{
"name": "PSO",
'points': -19
},
]
},
]
}
I want the max value of points from under the children sections. I mean from under technology and oil sectors.
What I've done so far:
var max;
for (var i in data.children.length) {
for (var j in data.data[i]) {
var point = data.data[i].children[j]
}
}
Try the following:
data = {
"name": "KSE100",
"children": [
{
"name": "TECHNOLOGY & COMMUNICATION",
"children": [
{
"name": "TRG",
'points': -21
},
{
"name": "SYS",
},
]
},
{
"name": "OIL",
"children": [
{
"name": "PPL",
'points': 9
},
{
"name": "PSO",
'points': -19
},
]
},
]
}
var array = [];
for (var first of data.children) {
for (var second of first.children) {
if(second.points != undefined)
{
array.push(second);
}
}
}
var maximumValue = Math.max.apply(Math, array.map(function(obj) { return obj.points; }));
console.log(maximumValue);
you can use the reduce method on the array object to do this
const maxValues = []
data.children.forEach(el => {
if (el.name === 'OIL' || el.name === 'TECHNOLOGY & COMMUNICATIO'){
const max = el.children.reduce((current, previous) => {
if (current.points > previous.points) {
return current
}
}, 0)
maxValues.append({name: el.name, value: max.points})
}
})
This will give you an array of the objects with the name and max value.
First you can convert your object to a string through JSON.stringify so that you're able to use a regular expression
(?<=\"points\":)-?\\d*
To matchAll the values preceded by the pattern \"points\": that are or not negative values. After it, convert the result to a array through the spread operator ... and then reduce it to get the max value.
const data = {name:"KSE100",children:[{name:"TECHNOLOGY & COMMUNICATION",children:[{name:"TRG",points:-21},{name:"SYS"}]},{name:"OIL",children:[{name:"PPL",points:9},{name:"PSO",points:-19}]}]};
console.log(
[ ...JSON.stringify(data).matchAll('(?<=\"points\":)-?\\d*')]
.reduce((acc, curr) => Math.max(curr, acc))
)
I wasn't 100% sure, what your exact goal is, so I included a grouped max value and and overall max value with a slight functional approach.
Please be aware that some functionalities are not working in older browsers i.e. flatMap. This should anyways help you get started and move on.
const data = {
name: "KSE100",
children: [
{
name: "TECHNOLOGY & COMMUNICATION",
children: [
{
name: "TRG",
points: -21,
},
{
name: "SYS",
},
],
},
{
name: "OIL",
children: [
{
name: "PPL",
points: 9,
},
{
name: "PSO",
points: -19,
},
],
},
],
};
const maxPointsByGroup = data.children.reduce(
(acc, entry) => [
...acc,
{
name: entry.name,
max: Math.max(
...entry.children
.map((entry) => entry.points)
.filter((entry) => typeof entry === "number")
),
},
],
[]
);
console.log("grouped max:", maxPointsByGroup);
const overallMax = Math.max(
...data.children
.flatMap((entry) => entry.children.flatMap((entry) => entry.points))
.filter((entry) => typeof entry === "number")
);
console.log("overall max:", overallMax);
I have this array in JSON format:
var result=[
{
"index": 13,
"id": 1122,
*
The approach below:
get a unique list of only the dates from the array i.e. dd/mm/yyyy
for each date in the unique list, create a sorted array per the times for that date
return the 0th item from that sorted array for that date
Example code:
var result = [
{
"index": 13,
"id": 1122,
"price": 100,
"dateTime": "11/12/2020 1:59"
},
{
"index": 14,
"id": 1122,
"price": 300,
"dateTime": "11/12/2020 3:15"
},
{
"index": 15,
"id": 1122,
"price": 314,
"dateTime": "11/13/2020 2:20"
},
{
"index": 16,
"id": 1122,
"price": 280,
"dateTime": "11/13/2020 2:23"
}
];
// get a list of the dates in result
// nothing fancy - the date is just a key
var dates = result.map(k => k.dateTime.substr(0, 10));
// get unique dates from this array
var uniqueDates = Array.from(new Set(dates));
// for each unique date, sort the times descending
// return the first item (latest) for that date
var filtered = uniqueDates.map(ud => {
var dateItems = result.filter(d => d.dateTime.substr(0, 10) == ud);
dateItems.sort((a, b) => (new Date(b.dateTime)).getTime() - (new Date(a.dateTime)).getTime());
return dateItems[0];
});
// output
console.log(filtered);
Sorting the Array on the Date value of dateTime, write a result array with dates-only as keys, retrieve the values of the result.
The TypeError, by the way, is because you should check for i + 1 being smaller than result.length in the loop (this will be falsy for the last element within the loop. In that case new Date(result[i+1].dateTime) will throw the error).
// initialize log helper
const log = Logger();
// create an empty Object
const result = {};
// sort data ascending
const dataSorted = getData().sort((a, b) =>
new Date(a.dateTime) - new Date(b.dateTime));
// add to result with datestring as key.
// The value with the most recent date will be preserved
// because key values are unique (so equal keys are overwritten)
dataSorted.forEach(v => result[new Date(v.dateTime).toDateString()] = v);
// the values of [result] contain the most recent records per date
log(Object.values(result));
// this can also be a one liner, using a reducer method
const resultX = Object.values(
getData()
.sort( (a, b) => new Date(a.dateTime) - new Date(b.dateTime) )
.reduce( (acc, value) =>
({...acc, [new Date(value.dateTime).toDateString()]: value}), {} )
);
log(`\n**from reducer`, resultX);
function getData() {
return [{
"index": 13,
"id": 1122,
"price": 100,
"dateTime": "11/12/2020 1:59"
},
{
"index": 14,
"id": 1122,
"price": 300,
"dateTime": "11/12/2020 3:15"
},
{
"index": 15,
"id": 1122,
"price": 314,
"dateTime": "11/13/2020 2:20"
},
{
"index": 16,
"id": 1122,
"price": 280,
"dateTime": "11/13/2020 2:23"
}
];
}
function Logger() {
const report = document.querySelector("#report") ||
document.body.insertAdjacentElement(
"beforeend",
Object.assign(document.createElement("pre"), {id: "report"}));
return (...args) => args.forEach(stuff =>
report.textContent += (stuff instanceof Object
? JSON.stringify(stuff, null, 2) : stuff) + "\n");
}
You can group the data based on date like,
const groups = data.reduce((groups, currVal) => {
const date = currVal.dateTime.split(' ')[0];
if (!groups[date]) {
groups[date] = [];
}
groups[date].push(currVal);
return groups;
}, {});
-> Here we split the date and and time part using split(' ') and took the date alone separately and form a group,
const date = currVal.dateTime.split(' ')[0];
And then you can get the recent date and time using the method,
groups[item].reduce((a, b) => (a.dateTime > b.dateTime ? a : b));
Working snippet:
const data = [
{
"index": 13,
"id": 1122,
"price": 100,
"dateTime": "11/12/2020 1:59"
},
{
"index": 14,
"id": 1122,
"price": 300,
"dateTime": "11/12/2020 3:15"
},
{
"index": 15,
"id": 1122,
"price": 314,
"dateTime": "11/13/2020 2:20"
},
{
"index": 16,
"id": 1122,
"price": 280,
"dateTime": "11/13/2020 2:23"
}
];
//Group the data based on date
const groups = data.reduce((groups, currVal) => {
const date = currVal.dateTime.split(' ')[0];
if (!groups[date]) {
groups[date] = [];
}
groups[date].push(currVal);
return groups;
}, {});
//Get the recent date and time based on each group
const result = [];
Object.keys(groups).filter(item => {
const newData = groups[item].reduce((a, b) => (a.dateTime > b.dateTime ? a : b));
result.push(newData);
});
console.log(result)
I can't set up an algo that counts my occurrences while respecting ESlint's 6 standards in javascript.
My input table is :
[
{
"id": 2,
"name": "Health",
"color": "0190fe"
},
{
"id": 3,
"name": "Agriculture",
"color": "0190fe"
},
{
"id": 1,
"name": "Urban planning",
"color": "0190fe"
},
{
"id": 1,
"name": "Urban planning",
"color": "0190fe"
}
]
And i want to get :
{"Urban planning": 2, "Health": 1, ...}
But that does not work with ESLINT / REACT compilation...
This is my code :
const jsonToIterate = *'MyPreviousInputJson'*
const names = []
jsonToIterate.map(item => (names.push(item.name)))
const count = []
names.forEach(item => {
if (count[item]){
count.push({text: item, value: 1})
} else {
count.forEach(function(top){top.text === item ? top.value =+ 1 : null})
}
})
Thank you so much
Well, you want an object in the end, not an array, so count should be {}. I also wouldn't use map if you're not actually returning anything from the call. You can use reduce for this:
let counts = topicsSort.reduce((p, c, i, a) => {
if (!p.hasOwnProperty(c.name)) p[c.name] = 0;
p[c.name]++;
return p;
}, {});
I'm half exppecting someone to close this as a duplicate because all you've asked for is a frequency counter. But here's an answer anyway:
const jsonToIterate = *'MyPreviousInputJson'*;
const names = {};
jsonToIterate.map(obj => {
if(obj.name in names){
names[obj.name]++
}
else{
names[obj.name] = 1;
}
})
I have object like below
[
{
"value": 14,
"name": "vwap"
},
{
"value": 1,
"name": "yopen"
},
{
"value": 12,
"name": "open"
},
{
"value": 13,
"name": "s3"
},
{
"value": 9,
"name": "fr1"
},
{
"value": 10,
"name": "fr2"
}
]
If my input is 9 , I need output as 1,9 and 10,12,13
If my input is 13 , I need output 1,9,10,12,13 and 14
Output should be 2 seperate objects like { "value": 10, "name": "fr2" } ,Also output should be sorted.
I tried something like below , but it works only for array.
function getVal(array, val, dir) {
for (var i=0; i < array.length; i++) {
if (dir == true) {
if (array[i] > val){
return array[i-1] || 0;
}
} else {
if (array[i] >= val) {
return array[i];
}
}
}
}
You can use filter() and check if given number is less or greater than objects value and use sort() in end
const arr = [ { "value": 14, "name": "vwap" }, { "value": 1, "name": "yopen" }, { "value": 12, "name": "open" }, { "value": 13, "name": "s3" }, { "value": 9, "name": "fr1" }, { "value": 10, "name": "fr2" } ]
function getParts(arr,num,min=0,max=Infinity){
let first = arr.filter(x => num >= x.value && x.value > min && x.value < max).sort((a,b) => a.value-b.value);
let second = arr.filter(x => num < x.value && x.value < max && x.value > min).sort((a,b) => a.value-b.value);
return [first,second];
}
console.log(getParts(arr,9,5,12))
console.log('----------For 13--------------')
console.log(getParts(arr,13))
Another way is to sort() the array first and then slice() it.
const arr = [ { "value": 14, "name": "vwap" }, { "value": 1, "name": "yopen" }, { "value": 12, "name": "open" }, { "value": 13, "name": "s3" }, { "value": 9, "name": "fr1" }, { "value": 10, "name": "fr2" } ]
function getParts(arr,num){
let temp = arr.slice().sort((a,b) => a.value - b.value);
let index = temp.findIndex(x => x.value === num);
return [temp.slice(0,index+1),temp.slice(index)];
}
console.log(JSON.parse(JSON.stringify(getParts(arr,9))))
console.log('----------For 13--------------')
console.log(JSON.parse(JSON.stringify(getParts(arr,13))))
You could take acheck and push the object into the wanted array.
function getParts(value) {
return data.reduce((r, o) => (r[+(o.value > value)].push(o), r), [[], []]);
}
var data = [{ value: 14, name: "vwap" }, { value: 1, name: "yopen" }, { value: 12, name: "open" }, { value: 13, name: "s3" }, { value: 9, name: "fr1" }, { value: 10, name: "fr2" }];
data.sort(({ value: a }, { value: b }) => a - b);
console.log(getParts(9));
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can use an object to store your result, containing both lower and upper parts.
Then, loop your array and compare the value against the input. You'll know where to store your element, in lower or upper part
let datas = [{"value":14,"name":"vwap"},{"value":1,"name":"yopen"},{"value":12,"name":"open"},{"value":13,"name":"s3"},{"value":9,"name":"fr1"},{"value":10,"name":"fr2"}];
function getVal(input)
{
let result =
{
lowerPart: [],
upperPart: []
};
datas.forEach((elem) =>
{
if (elem.value <= input)
result.lowerPart.push(elem);
else
result.upperPart.push(elem);
});
return result;
}
console.log(getVal(9));
console.log(getVal(13));
Using reduce()
var arr = [{"value":14,"name":"vwap"},{"value":1,"name":"yopen"},{"value":12,"name":"open"},{"value":13,"name":"s3"},{"value":9,"name":"fr1"},{"value":10,"name":"fr2"}]
function getVal(arr, find) {
return arr.reduce((acc, i) => {
acc[i.value <= find ? 'l' : 'g'].push(i)
return acc
}, {
l: [],
g: []
})
}
console.log(getVal(arr, 9))
console.log(getVal(arr, 13))
.as-console-wrapper { max-height: 100% !important; top: 0; }
Usage
let res = getVal(arr, 9)
res.l // lowerpart
res.g // greaterpart
You can use filter and sort function for your requirement.
var find = 9;
var left = arr.filter(c=>c.value <= find).sort((a,b) => a.value-b.value);
var right = arr.filter(c=>c.value > find).sort((a,b) => a.value-b.value);
var arr = [
{
"value": 14,
"name": "vwap"
},
{
"value": 1,
"name": "yopen"
},
{
"value": 12,
"name": "open"
},
{
"value": 13,
"name": "s3"
},
{
"value": 9,
"name": "fr1"
},
{
"value": 10,
"name": "fr2"
}
]
var find = 9;
var left = arr.filter(c=>c.value <= find).sort((a,b) => a.value-b.value);
var right = arr.filter(c=>c.value > find).sort((a,b) => a.value-b.value);
console.log('Less than or equal: ' + find);
console.log(left)
console.log('Greater than: ' + find);
console.log(right)
I am trying to aggregate and transform the following json :
[
{
"orderId" : "01",
"date" : "2017-01-02T06:00:00.000Z",
"items" : [
{
"itemId": 100,
"itemCost": 12,
"itemQuantity": 10
},
{
"itemId": 102,
"itemCost": 25,
"itemQuantity": 4
}
]
},
{
"orderId": "02",
"date" : "2017-01-08T06:00:00.000Z",
"items" : [
{
"itemId": 100,
"itemCost": 15,
"itemQuantity": 2
},
{
"itemId": 101,
"itemCost": 20,
"itemQuantity": 5
},
{
"itemId": 102,
"itemCost": 25,
"itemQuantity": 1
}
]
},
{
"orderId": "03",
"date" : "2017-02-08T06:00:00.000Z",
"items" : [
{
"itemId": 100,
"itemCost": 15,
"itemQuantity": 2
},
{
"itemId": 101,
"itemCost": 20,
"itemQuantity": 5
},
{
"itemId": 102,
"itemCost": 25,
"itemQuantity": 1
}
]
}]
into an object that is grouped by itemId, and then aggregated by quantity, and aggregated by total cost (item cost * item quantity for each order) by month. Example:
[
{
"itemId": 100,
"period": [
{
"month": "01/17",
"quantity": 12,
"cost": 130
}
]
},
{
"itemId": 101,
"period": [
{
"month": "01/17",
"quantity": 5,
"cost": 100
},
{
"month": "02/17",
"quantity": 5,
"cost": 100
}
]
},
{
"itemId": 102,
"period": [
{
"month": "01/17",
"quantity": 5,
"cost": 125
},
{
"month": "02/17",
"quantity": 1,
"cost": 25
}
]
}
]
I have a small indention on my desk in which I have been beating my head trying to figure how to do this using native map/reduce or lodash.
You can do like this:
var orders = [{orderId:"01",date:"2017-01-02T06:00:00.000Z",items:[{itemId:100,itemCost:12,itemQuantity:10},{itemId:102,itemCost:25,itemQuantity:4}]},{orderId:"02",date:"2017-01-08T06:00:00.000Z",items:[{itemId:100,itemCost:15,itemQuantity:2},{itemId:101,itemCost:20,itemQuantity:5},{itemId:102,itemCost:25,itemQuantity:1}]},{orderId:"03",date:"2017-02-08T06:00:00.000Z",items:[{itemId:100,itemCost:15,itemQuantity:2},{itemId:101,itemCost:20,itemQuantity:5},{itemId:102,itemCost:25,itemQuantity:1}]}];
// First, map your orders by items
var items = {};
orders.forEach(function(order) {
// set the month of each order
var month = new Date(order.date);
month = ('0' + (month.getMonth() + 1)).slice(-2) + '/' + String(month.getFullYear()).slice(-2);
// for each item in this order
order.items.forEach(function(item) {
// here we already have both keys: "id" and "month"
// then, we make sure they have an object to match
var id = item.itemId;
if (!items[id]) {
items[id] = {};
}
if (!items[id][month]) {
items[id][month] = { cost:0, quantity:0 };
}
// keep calculating the total cost
items[id][month].cost += item.itemCost * item.itemQuantity;
items[id][month].quantity += item.itemQuantity;
});
});
// Now, we format the calculated values to your required output:
var result = Object.keys(items).map(function(id) {
var obj = {
itemId: id,
period: Object.keys(items[id]).map(function(month) {
items[id][month].month = month;
return items[id][month];
}),
};
return obj;
});
console.log(result);
Hope it helps.
You could use this transformation:
const result = Object.values(myList.reduce( (acc, o) => {
const month = o.date.substr(5,2) + '/' + o.date.substr(2,2);
return o.items.reduce ( (acc, item) => {
const it = acc[item.itemId] || {
itemId: item.itemId,
period: {}
},
m = it.period[month] || {
month: month,
quantity: 0,
cost: 0
};
m.cost += item.itemCost * item.itemQuantity;
m.quantity += item.itemQuantity;
it.period[month] = m;
acc[item.itemId] = it;
return acc;
}, acc);
}, {})).map( o =>
Object.assign({}, o, { period: Object.values(o.period) })
);
const myList = [
{
"orderId" : "01",
"date" : "2017-01-02T06:00:00.000Z",
"items" : [
{
"itemId": 100,
"itemCost": 12,
"itemQuantity": 10
},
{
"itemId": 102,
"itemCost": 25,
"itemQuantity": 4
}
]
},
{
"orderId": "02",
"date" : "2017-01-08T06:00:00.000Z",
"items" : [
{
"itemId": 100,
"itemCost": 15,
"itemQuantity": 2
},
{
"itemId": 101,
"itemCost": 20,
"itemQuantity": 5
},
{
"itemId": 102,
"itemCost": 25,
"itemQuantity": 1
}
]
},
{
"orderId": "03",
"date" : "2017-02-08T06:00:00.000Z",
"items" : [
{
"itemId": 100,
"itemCost": 15,
"itemQuantity": 2
},
{
"itemId": 101,
"itemCost": 20,
"itemQuantity": 5
},
{
"itemId": 102,
"itemCost": 25,
"itemQuantity": 1
}
]
}];
const result = Object.values(myList.reduce( (acc, o) => {
const month = o.date.substr(5,2) + '/' + o.date.substr(2,2);
return o.items.reduce ( (acc, item) => {
const it = acc[item.itemId] || {
itemId: item.itemId,
period: {}
},
m = it.period[month] || {
month: month,
quantity: 0,
cost: 0
};
m.cost += item.itemCost * item.itemQuantity;
m.quantity += item.itemQuantity;
it.period[month] = m;
acc[item.itemId] = it;
return acc;
}, acc);
}, {})).map( o =>
Object.assign({}, o, { period: Object.values(o.period) })
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I think the other answers out there do a pretty good job from the vanilla angle, so I wanted to take a stab at a more lodash-intensive approach since you mentioned it as a tag. This is mainly just a fun challenge, but I hope the solution is elegant enough for you to lift components from.
Before we begin, I'll be using both the vanilla lodash module and the functional programming flavor of lodash. Let fp be the functional programming module and _ be vanilla (and let orders be your original data structure). Also, as a challenge, I'll do my best to minimize vanilla JS methods and arrow funcs to maximize lodash methods and function creation methods.
First, let's get all the items in a row, paired with their order information:
const items = _.flatMap(orders, o=> _.map(o.items, i=> [i, o]));
I know I said I wanted to minimize arrow functions, but I couldn't think of any other way to get the order object to the end of the chain. Challenge yourself to rewrite the above in terms of a composition (e.g. fp.compose or _.flow) and see what happens.
I'd say now's as good a time as any to group up our pairs by the item id:
const id_to_orders = _.groupBy(items, fp.get('[0].itemId'));
Here, fp.get('[0].itemId') gives us a function which, given an array, returns the itemId of the first element (in our case, we have a list of pairs, the first element of which is the item, the second of which is the relevant order object). Therefore, id_to_orders is a map from an item's ID to a list of all the times it was ordered.
This id_to_orders map looks pretty close to the data structure we're after. At a high level, all that's left is transforming the order data for each item into the quantity and cost, grouped by month.
const result = _.mapValues(id_map, fp.flow(
// Arrange the item's orders into groups by month
fp.groupBy(month)
// We're done with the order objects, so fp.get('[0]') filters them
// out, and the second function pairs the item's cost and quantity
, fp.mapValues(fp.flow(
fp.map(fp.flow(fp.get('[0]'), i=> [i.itemCost, i.itemQuantity]))
// Sum up the cost (left) and quantity (right) for the item for the month
, fp.reduce(add_pair, [0, 0])))
// These last couple lines just transform the resulting data to look
// closer to the desired structure.
, _.toPairs
, fp.map(([month, [cost, count]])=> ({month, cost, count}))
));
And the helpers month and add_pair referenced above:
function month([item, order]){
const date = new Date(order.date)
, month = date.getMonth() + 1
, year = date.getFullYear().toString().slice(-2);
return `${month}/${year}`;
}
function add_pair(p1, p2){
return [p1[0] + p2[0], p1[1] + p2[1]];
}
Just out of curiosity (or sadism), let's see what this whole thing would look like chained together as a single pipeline:
const get_order_data = fp.flow(
fp.flatMap(o=> _.map(o.items, i=> [i, o]))
, fp.groupBy(fp.get('[0].itemId'))
, fp.mapValues(fp.flow(
fp.groupBy(month)
, fp.mapValues(fp.flow(
fp.map(fp.flow(fp.get('[0]'), i=> [i.itemCost, i.itemQuantity]))
, fp.reduce(add_pair, [0, 0])))
, _.toPairs
, fp.map(([month, [cost, count]])=> ({month, cost, count})))
));
const result = get_order_data(orders);
You'll notice this composed version has a lot more fp (as opposed to _). If you're curious why it's easier this way, I encourage you to read the lodash FP guide.
jsfiddle with everything.
Finally, if you'd like to transform the result from the code above exactly into the output format you mentioned in your post, here's what I recommend:
const formatted = _.keys(result).map(k=> ({itemId: k, periods: result[k]}));