I have a score array containing two objects: Liga and Premier. These 2 objects are an array of a list of teams.
I was able to define the greater string when score was previously a single array of objects.
This is the demo i have reproduced where the comparison works fine.
This is the code calculating the higher value comparing the 2 objects.
const maxAverage = teams => {
return teams.map(team => {
return {
team:team,
avg: getAverage(team)
}
}).reduce((a,b)=>a.avg>b.avg?a:b).team
}
<p>Stronger Team:{maxAverage([this.state.homeCity,this.state.awayCity])</p>
The problem now is that now score is an array of the 2 object as i said and i am trying to change my function in something like
const maxAverage = (league, teams) => {
return teams.map(team => {
return {
team:team,
avg: getAverage(league,team)
}
}).reduce((a,b)=>a.avg>b.avg?a:b).team
}
I am not able to pass to my function maxAverage the parameter of one of the two leagues selected and then the 2 objects ( teams ) i want to compare.
i want to do something like this:
<p>Stronger Team:{maxAverage([this.state.selectedLeague], this.state.selectedHomeTeam,this.state.selectedAwayTeam])}
This is the other demo i have reproduced with the current situation.
Given the signature const maxAverage = (league, teams) => ..., following code would match the expected arguments (not sure about the business logic though):
maxAverage(
this.state.selectedLeague,
[this.state.selectedHomeTeam, this.state.selectedAwayTeam]
)
I looked at your second demo and I think you have two choices to get the correct team selected and you can reuse your previous getAverage method for both. Either
const maxAverage = (league, teams) => {
const currentLeague = [scores][0][league]
return teams
.map(team => {
return {
team: team,
avg: getAverage(currentLeague, team)
};
})
.reduce((a, b) => (a.avg > b.avg ? a : b)).team;
};
alternatively you could keep the original maxAverage code and change how you implement the league value eg.
<p>
Stronger Team:
{maxAverage(scores[this.state.selectedLeague], [
this.state.selectedHomeTeam,
this.state.selectedAwayTeam
])}
</p>
Why not simply extract team when selected, save in the state and use the same method used before?
What is a problem?
const scores = {'liga':[
{ day: "1", Barcelona: 1, Real: 3, Valencia: 0 },
{ day: "2", Barcelona: 4, Real: 6, Valencia: 3 },
{ day: "3", Barcelona: 7, Real: 7, Valencia: 3 },
{ day: "4", Barcelona: 7, Real: 8, Valencia: 6 }
], 'primier':[
{ day: "1", Barcelona: 1, Real: 3, Valencia: 0 },
{ day: "2", Barcelona: 4, Real: 6, Valencia: 3 },
{ day: "3", Barcelona: 7, Real: 7, Valencia: 3 },
{ day: "4", Barcelona: 7, Real: 8, Valencia: 6 }]};
const getAverage = (type, team) => {
if (isNaN(scores[type][0][team])) return null;
return scores[type].map(x => x[team]).reduce((a, c) => a + c) / scores[type].length;
};
getAverage('liga',this.state.homeCity);
src:
https://codesandbox.io/s/recharts-examples-d9qy0
Related
I have some data like this, an array of objects:
source = [{
day: 1,
deliveries: 16,
hours: 9
}, {
day: 2,
deliveries: 19,
hours: 11
}]
Which I would like to have in this format:
source = (['day', 'deliveries', 'hours'],
['1', '16', '9'],
['2', '19', '11'])
Sort of like a table. I read up a little on mapping arrays and tried this:
const datatable = source.map(d => Array.from(Object.keys(d)))
console.log(datatable)
// [["day", "deliveries", "hours"], ["day", "deliveries", "hours"]]
And this:
const datatable = source.map(d => Array.from(Object.values(d)))
console.log(datatable)
// [[1, 16, 9], [2, 19, 11]]
Each gives me half of what I want.
I tried this:
let datatable = source.map(d => Array.from(Object.keys(d)))
let datatable2 = source.map(d => Array.from(Object.values(d)))
datatable = datatable[1]
let combined = datatable.concat(datatable2);
console.log(combined)
///["day", "deliveries", "hours", [1, 16, 9], [2, 19, 11]]
But even here the column names are not being combined correctly, and this way seems a little messy. How do I have the keys be on top (like column names would be) and the values following them?
Assuming you want source to be an array of arrays (common for table-like structures), get the keys once, then add each row array mapping the object's properties to the key for that index:
const keys = Object.keys(source[0]);
const result = [keys, ...source.map(obj => keys.map(key => obj[key]))];
Live Example:
const source = [{
day: 1,
deliveries: 16,
hours: 9
}, {
day: 2,
deliveries: 19,
hours: 11
}];
const keys = Object.keys(source[0]);
const result = [keys, ...source.map(obj => keys.map(key => obj[key]))];
console.log(result);
.as-console-wrapper {
max-height: 100% !important;
}
Note that this assumes a couple of things:
The source array always has at least one object in it.
The objects in the source array all have the same set of properties.
You want the keys to be in the order in which they appear in the first object in the array. (JavaScript object properties do have a defined order now, but using that order is almost never a good idea, so you might want to do some kind of sort operation on keys.)
You are almost there. Just use [0] to get the first item to get only keys. And then combine the array using ....
let source = [{
day: 1,
deliveries: 16,
hours: 9
}, {
day: 2,
deliveries: 19,
hours: 11
}]
let ansArray = Object.keys(source[0]);
let datatable = source.map(d => Array.from(Object.values(d)))
let combined = [ansArray,...datatable];
console.log(combined);
Array.from(Object.keys(d)) and Array.from(Object.values(d)) will return the wrong data if the order of property is wrong.
const source = [{
day: 1,
deliveries: 16,
hours: 9
}, {
day: 2,
deliveries: 19,
hours: 11
}]
const datatable = source.reduce((acc, item, index) => {
if (index == 0) {
acc.push(Object.keys(item));
}
const newRow = acc[0].reduce((rowAcc, prop) => {
rowAcc.push(item[prop]);
return rowAcc;
}, []);
acc.push(newRow);
return acc;
}, [])
console.log(datatable);
I've got an example array that I'm trying to reduce by the counts of the occurrence of a key (sentiment in this example):
const sentimentLog = [
{
id: 1,
createdOn: new Date('2020-02-13'),
sentiment: 1
},
{
id: 2,
createdOn: new Date('2020-02-12'),
sentiment: 1
},
{
id: 3,
createdOn: new Date('2020-02-12'),
sentiment: 2
},
{
id: 4,
createdOn: new Date('2020-02-11'),
sentiment: 3
},
{
id: 5,
createdOn: new Date('2020-02-11'),
sentiment: 2
},
{
id: 6,
createdOn: new Date('2020-02-10'),
sentiment: 1
},
{
id: 7,
createdOn: new Date('2020-02-10'),
sentiment: 2
},
{
id: 8,
createdOn: new Date('2020-02-09'),
sentiment: 1
}
]
I'm using:
const sentimentGrouped = (sentiments) => {
return sentiments.reduce((hash, { sentiment }) => {
hash[sentiment] = (hash[sentiment] || 0) + 1
return hash
}, [])
}
And it's nearly there. What I can't figure out is how to replace undefined when there's no sentiment scores of 0 (which is a possibility).
console.log('sentimentGrouped', sentimentGrouped(sentimentLog))
The above produces:
"sentimentGrouped" [undefined, 4, 3, 1]
Whereas I'd like:
"sentimentGrouped" [0, 4, 3, 1]
What am I missing?
Thanks in advance.
Edit: I'll elaborate a bit further, there's 4 scores that will be returned (0 to 3). The data returned will be based on a date range. So there may be instances where there'll be no 1s returned, similarly no 3s returned by a different date range.
The issue is that if you never touch an element of the array, then it stays as a hole in the array, which means it's treated as undefined. Since you know the length of the array i would just prefill the array with zeros. Any sentiment score that does occur will be incremented. Any one that doesn't will stay with its initial value.
return sentiments.reduce((hash, { sentiment }) => {
hash[sentiment] = hash[sentiment] + 1
return hash
}, [0, 0, 0, 0])
Question: How can I reformat this JSON array by "grouping" via different keys, using ReactJS?
I have a JSON array as :
[
{Product: "Shoes", Sold: 5, Bought : 0, Reversed : 2} ,
{Product: "Table", Sold: 2, Bought : 0, Reserved : 4}
]
The reason for this is the data type I'm working with, and on realizing I need to visualize this data in a different way (due to one of the graph packages I am using) I need to structure this data as:
[
{
Status: "Sold",
Shoes : 5,
Table : 2
} ,
{
Status: "Bought",
Shoes : 0,
Table : 0
} ,
{
Status: "Reserved",
Shoes : 2,
Table : 4
}
]
So I'm grouping the data into the keys other than Product, and then the keys after this are Product with the Value being the Product and it's "status".
Frankly, I am at a complete loss as to what to do, as I'm thinking the code required to generate this would be quite convoluted, so I'm very open to know if this just is too much work.
const data = [
{
Product: "Shoes",
Sold: 5,
Bought : 0,
Reserved : 2
} , {
Product: "Table",
Sold: 2,
Bought : 0,
Reserved : 4
}
];
let resultData = [];
Object.keys(data[0]).forEach((key, idx) => {
if (idx !== 0) {
let resultUnit = {
Status: key,
};
data.forEach(item => {
return resultUnit = {
...resultUnit,
[item.Product]: item[key],
}
})
resultData.push(resultUnit);
}
})
console.log(resultData);
// 0: {Status: "Sold", Shoes: 5, Table: 2}
// 1: {Status: "Bought", Shoes: 0, Table: 0}
// 2: {Status: "Reserved", Shoes: 2, Table: 4}
You can do this using the Array.reduce function. (Actually, two reduce functions).
Here's an extensible solution that allows for other statuses.
Note that I changed everything to lowercase, as is standard convention.
const items = [
{product: "Shoes", sold: 5, bought : 0, reserved : 2} ,
{product: "Table", sold: 2, bought : 0, reserved : 4}
]
//We declare the status types here.
const keys = ["sold", "bought", "reserved"];
// Just create the initial 'statuses' array.
function initAcc(keys) {
return keys.map((key) => {
return {
status: key
}
});
}
//Here we are iterating over each item, getting it to return a single accumulator array each time.
const newItems = items.reduce((acc, cur) => {
return addItemToAccumulator(acc, cur);
}, initAcc(keys));
console.log(newItems);
// This function maps of the accumulator array (ie. over each status).
function addItemToAccumulator(acc, item) {
return acc.reduce((acc, statusLine) => {
//Find the count from the existing status if it exists,
//Add the current items count for that status to it.
const itemCount = item[statusLine.status] + (statusLine[item.product] || 0);
//Return a modified status, with the new count for that product
return [
...acc,
{
...statusLine,
[item.product]: itemCount
}
];
}, []);
}
Lets just do a simple loop function and create a couple objects to clearly solve the problem here:
const data = [YOUR_INITIAL_ARRAY];
let Sold, Bought, Reserved = {};
data.forEach(({Product, Sold, Bought, Reserved})=> {
Sold[Product] = Sold;
Bought[Product] = Bought;
Reservered[Product] = Reserved;
});
let newArray = [Sold, Bought, Reserved];
I think you can see where this is going ^ I see a few others have given complete answers, but try and go for the clear understandable route so it makes sense.
All you have to do after this is set the status which i'd do off an enum and you are good
I need to create one single array from 3 arrays,
I already implemented the logic and it's working but i think with Array.prototype i can achieve the same with better performance
let classrooms = [
1,
2
]
let modules = [
5,
6
]
let weeks = [
7,
8
]
let avalArray = [];
classrooms.forEach(classroomId => {
modules.forEach(moduleId => {
weeks.forEach(week => {
avalArray.push({
classroomId: classroomId,
moduleId: moduleId,
week: week
});
});
});
});
This is the expected output:
[ { classroomId: 1, moduleId: 5, week: 7 },
{ classroomId: 1, moduleId: 5, week: 8 },
{ classroomId: 1, moduleId: 6, week: 7 },
{ classroomId: 1, moduleId: 6, week: 8 },
{ classroomId: 2, moduleId: 5, week: 7 },
{ classroomId: 2, moduleId: 5, week: 8 },
{ classroomId: 2, moduleId: 6, week: 7 },
{ classroomId: 2, moduleId: 6, week: 8 } ] ```
There was a request some time ago for the cartesian product back in #852 (January 2015!). As you see, it's not implemented.
As the others said, performing a simple loop without external arrays will definitely be faster. To be sure: just benchmark it. I've prepared a simple suite on perf.link and here are the results:
for-loop: 175us
for-of-loop: 175us
forEach: 290us
map-map-map-flat: 465us
flatMap: 5635us
Exact numbers are not important here, but here's one takeaway: for-of loop (not transpiled!) is one of the fastest and still very elegant:
const result = [];
for (const classroomId of classrooms)
for (const moduleId of modules)
for (const week of weeks)
result.push({classroomId, moduleId, week});
This is a more functional solution that uses Array.flatMap() with Array.map() to generate the array of objects.
However, I think that your performance should be better, since yours doesn't generate temporal arrays that are then flattened to other temporal arrays, that are flattened to a single array.
const classrooms = [1, 2]
const modules = [5, 6]
const weeks = [7, 8]
const result = classrooms.flatMap(classroomId =>
modules.flatMap(moduleId =>
weeks.map(week => ({
classroomId,
moduleId,
week
}))))
console.log(result)
You can use map and flat. It will be more compact, but it will be very slow. Use for loop for better performance.
classrooms
.map(classroomId => modules
.map(moduleId => weeks.map(week => ({classroomId, moduleId, week})))
).flat(2)
There is an equals function in Ramdajs which is totally awesome, it will provide the following:
// (1) true
R.equals({ id: 3}, { id: 3})
// (2) true
R.equals({ id: 3, name: 'freddy'}, { id: 3, name: 'freddy'})
// (3) false
R.equals({ id: 3, name: 'freddy'}, { id: 3, name: 'freddy', additional: 'item'});
How would I go about enhancing this function, or in some other way produce a true result for number 3
I would like to ignore all the properties of the rValue not present in the lValue, but faithfully compare the rest. I would prefer the recursive nature of equals remain intact - if that's possible.
I made a simple fiddle that shows the results above.
There's a constraint on equals in order to play nicely with the Fantasy Land spec that requires the symmetry of equals(a, b) === equals(b, a) to hold, so to satisfy your case we'll need to get the objects into some equivalent shape for comparison.
We can achieve this by creating a new version of the second object that has had all properties removed that don't exist in the first object.
const intersectObj = (a, b) => pick(keys(a), b)
// or if you prefer the point-free edition
const intersectObj_ = useWith(pick, [keys, identity])
const a = { id: 3, name: 'freddy' },
b = { id: 3, name: 'freddy', additional: 'item'}
intersectObj(a, b) // {"id": 3, "name": "freddy"}
Using this, we can now compare both objects according to the properties that exist in the first object a.
const partialEq = (a, b) => equals(a, intersectObj(a, b))
// again, if you prefer it point-free
const partialEq_ = converge(equals, [identity, intersectObj])
partialEq({ id: 3, person: { name: 'freddy' } },
{ id: 3, person: { name: 'freddy' }, additional: 'item'})
//=> true
partialEq({ id: 3, person: { name: 'freddy' } },
{ id: 3, person: { age: 15 }, additional: 'item'})
//=> false
Use whereEq
From the docs: "Takes a spec object and a test object; returns true if the test satisfies the spec, false otherwise."
whereEq({ id: 3, name: 'freddy' }, { id: 3, name: 'freddy', additional: 'item' })
The other way around is to develop your own version. It boils down to:
if (is object):
check all keys - recursive
otherwise:
compare using `equals`
This is recursive point-free version that handles deep objects, arrays and non-object values.
const { equals, identity, ifElse, is, mapObjIndexed, useWith, where } = R
const partialEquals = ifElse(
is(Object),
useWith(where, [
mapObjIndexed(x => partialEquals(x)),
identity,
]),
equals,
)
console.log(partialEquals({ id: 3 }, { id: 3 }))
console.log(partialEquals({ id: 3, name: 'freddy' }, { id: 3, name: 'freddy' }))
console.log(partialEquals({ id: 3, name: 'freddy' }, { id: 3, name: 'freddy', additional: 'item' }))
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>
I haven't used Ramda.js before so if there's something wrong in my answer please be free to point out.
I learned the source code of Ramda.js
In src/equals.js, is where the function you use is defined.
var _curry2 = require('./internal/_curry2');
var _equals = require('./internal/_equals');
module.exports = _curry2(function equals(a, b) {
return _equals(a, b, [], []);
});
So it simply put the function equals (internally, called _equals) into the "curry".
So let's check out the internal _equals function, it did check the length in the line 84~86:
if (keysA.length !== keys(b).length) {
return false;
}
Just comment these lines it will be true as you wish.
You can 1) just comment these 3 lines in the distributed version of Ramda, or 2) you can add your own partialEquals function to it then re-build and create your version of Ramda (which is more recommended, from my point of view). If you need any help about that, don't hesitate to discuss with me. :)
This can also be accomplished by whereEq
R.findIndex(R.whereEq({id:3}))([{id:9}{id:8}{id:3}{id:7}])