JavaScript Mapping Object To Array - javascript

I would like to convert objects in JavaScript, but I'm not really sure of the best way to do it. I don't often code in the language so I don't really know much of the fundamentals- this is the object I get back from an API call in a React project:
{
"api": {
"results": 380,
"fixtures": [
{
"fixture_id": 65,
"league_id": 2,
"league": {
"name": "Premier League",
"country": "England",
"logo": "https://media.api-sports.io/football/leagues/2.png",
"flag": "https://media.api-sports.io/flags/gb.svg"
},
"event_date": "2018-08-10T19:00:00+00:00",
"event_timestamp": 1533927600,
"firstHalfStart": 1533927600,
"secondHalfStart": 1533931200,
"round": "Regular Season - 1",
"status": "Match Finished",
"statusShort": "FT",
"elapsed": 90,
"venue": "Old Trafford (Manchester)",
"referee": null,
"homeTeam": {
"team_id": 33,
"team_name": "Manchester United",
"logo": "https://media.api-sports.io/football/teams/33.png"
},
"awayTeam": {
"team_id": 46,
"team_name": "Leicester",
"logo": "https://media.api-sports.io/football/teams/46.png"
},
"goalsHomeTeam": 2,
"goalsAwayTeam": 1,
"score": {
"halftime": "1-0",
"fulltime": "2-1",
"extratime": null,
"penalty": null
}
}
]
}
}
I would like to convert it to this array (the array holds multiple objects):
[
{
"homeTeam": {
"id": 33,
"name": "Manchester United",
"teamId": 33
},
"awayTeam": {
"id": 46,
"name": "Leicester",
"teamId": 46
},
"outcome": {
"goalsScoredByAwayTeam": 2,
"goalsScoredByHomeTeam": 1
},
"resulted": true,
"type": "LEAGUE"
}
]
The teamId and id actually need to lookup another object before the final output.
I'm not sure what the best way to do it is. This is my function so far, trying to make use of optional chaining:
function convertFixturesToArray() {
fixturesStore.getFixtures()?.api?.fixtures?.length ? fixtures.api.fixtures.map(fixture => (
//TRANSFORMATION GOES IN HERE
)) : null;
}

You seem on the right track. It should be something like this (written in a slightly more modern JS)
convertFixturesToArray = () => fixturesStore.getFixtures()?.api?.fixtures?.map?.(fixture => {
//Do whatever check you need here with the fixture object
return {
homeTeam: { ...fixture.homeTeam },
awayTeam: { ...fixture.awayTeam },
outcome: {
goalsScoredByAwayTeam: fixture.goalsAwayTeam,
goalsScoredByHomeTeam: fixture.goalsHomeTeam,
},
type: 'LEAGUE',
resulted: true,
},
}) ?? [];

It looks like you're trying to get certain key/value pairs from your api response. With a mix of map, reduce, and find, you can get the values you're looking for by defining them in an array (i.e. desiredProps).
Of course, adding the "outcome" field and your other hardcoded fields would require a bit more logic on top of this. Boris' answer addresses this problem. I've taken a more flexible approach.
let apiResult = {
"fixtures": [
{
"prop1": "a1",
"prop2": "a2",
"prop3": "a3"
},
{
"prop1": "b1",
"prop2": "b2",
"prop3": "b3"
}
]
}
let desiredProps = ["prop2", "prop3"]
let result = apiResult.fixtures.map(x => {
return Object.keys(x).reduce((acc, curr) => {
if (desiredProps.find(y => y === curr)) {
acc[curr] = x[curr]
}
return acc
}, {})
})
console.log(result)

Related

Set new Value from mapping inside Map() and using reducer to return Boolean or Object to a specific new value using Javascript

I want to assign a new Value (let say this is a flag such as isActive) from an existing object. this is an example for the object I'm using to try:
let localValue = {
"id": "019eadd3-2e71-4446-a195-69d849d88a43",
"discount": {
"code": "PFMQWERTY",
"id": "019eadd3-2e71-4446-a195-69d849d88a43",
"isActive": false
},
"discountId": "019eadd3-2e71-4446-a195-69d849d88a43",
"discountRules": [
{
"id": "1-1",
"type": "FIXED",
"value": 30000,
"package": {
"id": "1-1-A",
"name": "Package A",
"maxDiscountInApps": 3,
"discountInApps": [
{
"id": "1-1-A-A",
"code": "QWERTY",
"expirationDate": "2034-02-28T00:00:00+0000"
}
]
}
},
{
"id": "1-2",
"type": "FIXED",
"value": 100000,
"package": {
"id": "1-2-A",
"name": "Package B",
"maxDiscountInApps": 3,
"discountInApps": [
{
"id": "1-2-A-A",
"code": "KASH",
"expirationDate": "2032-02-03T00:00:00+0000"
}
]
}
},
{
"id": "1-3",
"type": "FIXED",
"value": 15000,
"package": {
"id": "1-3-A",
"name": "Package C",
"maxDiscountInApps": 3,
"discountInApps": []
}
},
{
"id": "1-4",
"type": "FIXED",
"value": 180000,
"package": {
"id": "1-4-A",
"name": "Package D",
"maxDiscountInApps": 3,
"discountInApps": []
}
},
{
"id": "1-5",
"type": "FIXED",
"value": 15000,
"package": {
"id": "1-5-A",
"name": "",
"maxDiscountInApps": 3,
"discountInApps": []
}
},
{
"id": "1-6",
"type": "FIXED",
"value": 30003,
"package": {
"id": "1-6-A",
"name": "Package E",
"maxDiscountInApps": 3,
"discountInApps": [
{
"id": "1-6-A-A",
"code": "QWERTY",
"expirationDate": "2034-02-28T00:00:00+0000"
},
{
"id": "1-6-A-B",
"code": "KASH",
"expirationDate": "2032-02-03T00:00:00+0000"
},
{
"id": "1-6-A-C",
"code": "ANT",
"expirationDate": "2021-07-30T00:00:00+0000"
}
]
}
},
{
"id": "1-7",
"type": "FIXED",
"value": 5000,
"package": {
"id": "1-7-A",
"name": "Package F",
"maxDiscountInApps": 3,
"discountInApps": []
}
}
],
"expirationDate": "28/02/2034 07:00:00",
"totalPackagesShown": 2
}
the goals I want to achieve is to check if there is the same code in discountRules.package.discountInApps.code === this.localValue.discount.code then return true, if failed then return false.
I can already find the way to set it using map() and some() like this:
this.localValue.discountRules = this.localValue.discountRules.map(
rule => ({
...rule,
isActive:
rule.package &&
rule.package.discountInApps &&
rule.package.discountInApps.length !== 0
? rule.package.discountInApps.some(
ruleItems =>
ruleItems.code === this.localValue.discount.code
)
: false
})
);
but performance wise, is it better using map() and reducer() combination for this case or better stay using map() and some()? because I read it in here Array.prototype.reduce() it seems when using reducer can make an array reduce until it finds a value that I want (is my understanding correct?) so if it's true then...
is it better using this map() and reducer() (if its possible) or stay with map() and some() combination?
(I'm still failed to implement map() and reducer(), so can someone tell me how to use this (map() and reducer()) combination?)
Notes:
if by any chance there is a better way, I'm open for it
just in case, I'm already try to read this thread but still not quite understand as how to implement it in my case:
remove-object-from-array-using-javascript
If you prefer performance, the traditional for loop would be the fastest.
For execution times, I get these
Map & Some: 27ms average
Map & Reduce: 32ms average
You can use console.time() and console.timeEnd() to check for execution times.
The array Reduce method will run a callback function on each element of the supplied array and will return the calculated value from the callback (the value will be stored in the index of the element). If you want to use reduce with map, you can do something like this
localValue.discountRules = localValue.discountRules.map((rule) => ({
...rule,
isActive:
rule.package &&
rule.package.discountInApps &&
rule.package.discountInApps.length !== 0
? rule.package.discountInApps.reduce(
(accumulatedValue, currentValue) =>
accumulatedValue && currentValue.code === localValue.discount.code,
true // this is the default initial value for the accumulatedValue
)
: false,
}));
Alternatively, you can use optional chaining (?.) Like this
localValue.discountRules = localValue.discountRules.map((rule) => ({
...rule,
isActive:
rule?.package?.discountInApps?.some(
(ruleItems) => ruleItems.code === localValue.discount.code
)
}));
(Do check out the supported browsers here).

Transform array of data into grouped data for SectionList component

I'll freely admit that Javascript is not my strongest language, and React Native is very new, so, there may be an obviously easy way to do this that I'm not seeing.
I've got an API that presents some transaction data in a simple structure:
[
{
"id": 1,
"title": "Apple Store",
"date": "2021-09-10",
"amount": "$100.00",
},
{
"id": 41,
"title": "Zulauf, Walter and Metz",
"date": "2021-09-10",
"amount": "$14.00",
},
{
"id": 9,
"title": "Aufderhar PLC",
"date": "2021-09-09",
"amount": "$78.00",
},
{
"id": 10,
"title": "Bayer and Sons",
"date": "2021-09-07",
"amount": "$67.00",
}
]
I want to present this data using a SectionList component, with the transactions in sections by date. My (likely crude) attempt to solve this was going to be to transform this data into the following structure:
[
{
"date": "2021-09-10",
"transactions": [
{
"id": 1,
"title": "Apple Store",
"date": "2021-09-10",
"amount": "$100.00",
},
{
"id": 41,
"title": "Zulauf, Walter and Metz",
"date": "2021-09-10",
"amount": "$14.00",
}
]
},
{
"date": "2021-09-09",
"transactions": [
{
"id": 9,
"title": "Aufderhar PLC",
"date": "2021-09-09",
"amount": "$78.00",
}
]
},
{
"date": "2021-09-07",
"transactions": [
{
"id": 10,
"title": "Bayer and Sons",
"date": "2021-09-07",
"amount": "$67.00",
}
]
}
]
But I'm honestly lost as to how to transform this data (or if there's a better way to solve this problem). I started by using Lodash's groupBy function, which seemed promising, but it looks like SectionList doesn't want an object, it wants an array.
Transforming the output of groupBy into an array straight off drops the keys and I've got grouped data but no clear value for the section header.
Again, there's probably some deviously simple way to address this, data comes in as a flat array all the time. I appreciate any guidance, assistance, or examples anybody can point me to.
const input = [
{
"id": 1,
"title": "Apple Store",
"date": "2021-09-10",
"amount": "$100.00",
},
{
"id": 41,
"title": "Zulauf, Walter and Metz",
"date": "2021-09-10",
"amount": "$14.00",
},
{
"id": 9,
"title": "Aufderhar PLC",
"date": "2021-09-09",
"amount": "$78.00",
},
{
"id": 10,
"title": "Bayer and Sons",
"date": "2021-09-07",
"amount": "$67.00",
}
]
const result = input.reduce((accum, current)=> {
let dateGroup = accum.find(x => x.date === current.date);
if(!dateGroup) {
dateGroup = { date: current.date, transactions: [] }
accum.push(dateGroup);
}
dateGroup.transactions.push(current);
return accum;
}, []);
console.log(result)
Given an array, whenever your result is expecting to have same number of elements, use map, but since your result has different number of elements, use reduce as shown above. The idea is by having reduce, loop over each element, see if you can find the element, and push the current element into the list
The lodash groupBy just helps you with group data, you should process grouped data by converting it into your format.
const input = [
{
"id": 1,
"title": "Apple Store",
"date": "2021-09-10",
"amount": "$100.00",
},
{
"id": 41,
"title": "Zulauf, Walter and Metz",
"date": "2021-09-10",
"amount": "$14.00",
},
{
"id": 9,
"title": "Aufderhar PLC",
"date": "2021-09-09",
"amount": "$78.00",
},
{
"id": 10,
"title": "Bayer and Sons",
"date": "2021-09-07",
"amount": "$67.00",
}
];
const groupedArray = _.groupBy(input, "date");
let result = [];
for (const [key, value] of Object.entries(groupedArray)) {
result.push({
'date': key,
'transactions': value
})
}
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>
simply
const data =
[ { id: 1, title: 'Apple Store', date: '2021-09-10', amount: '$100.00' }
, { id: 41, title: 'Zulauf, Walter and Metz', date: '2021-09-10', amount: '$14.00' }
, { id: 9, title: 'Aufderhar PLC', date: '2021-09-09', amount: '$78.00' }
, { id: 10, title: 'Bayer and Sons', date: '2021-09-07', amount: '$67.00' }
]
const res = Object.entries(data.reduce((r,{id,title,date,amount})=>
{
r[date] = r[date] ?? []
r[date].push({id,title,date,amount})
return r
},{})).map(([k,v])=>({date:k,transactions:v}))
console.log( res )
.as-console-wrapper { max-height: 100% !important; top: 0 }
With lodash you can group by the date then map to the required form:
const input = [{"id":1,"title":"Apple Store","date":"2021-09-10","amount":"$100.00"},{"id":41,"title":"Zulauf, Walter and Metz","date":"2021-09-10","amount":"$14.00"},{"id":9,"title":"Aufderhar PLC","date":"2021-09-09","amount":"$78.00"},{"id":10,"title":"Bayer and Sons","date":"2021-09-07","amount":"$67.00"}];
const result = _.map(
_.groupBy(input, 'date'),
(transactions, date) => ({ date, transactions })
)
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>
you could use loadash
var result = _(data)
.groupBy(item => item.date)
.map((value, key) => ({date: key, transactions: value}))
.value();

Object has many arrays, loop through each and change some value

I have an object that has a whole host of arrays and properties. There is a property called targetProperty which appears in various places of the object.
I have a function where if the user clicks yes, every instance of that property needs to be reassigned to a new value.
The problem is the function that I used for assigning a new value doesn't work in this senario:
reassingPropertyInObj(obj, status) {
if (typeof obj === 'object' && obj !== null) {
obj.targetProperty = status;
for (const key in obj) {
this.handleExpandCollapseClick(obj[key], status);
}
}
},
Does anyone have a solution for this? Also can't use JSON.parse() or anything like that because the properties need to stay reactive for later reassignment if needed by the user.
Below is an example of one object:
{
"id": 16,
"ref_study_id": "3412333",
"title": "SomePersonNameOne",
"capabilities_available": [
{
"id": 75,
"name": "Clinical Data",
},
{
"id": 538,
"name": "RK's Capability",
}
],
"capabilities_impacted": [],
"businessImpact": {
"id": 2,
"name": "Medium"
},
"sites_impacted": [],
"sites_available": []
},
{
"id": 6,
"ref_study_id": "123124",
"title": null,
"capabilities_available": [
{
"id": 37,
"name": "Clinical Site Experience,
},
{
"id": 41,
"name": "Experience",
}
],
"capabilities_impacted": [
{
"id": 37,
"name": "Information Exchange",
"is_study_level": false,
"businessImpact": {
"id": 2,
"name": "Medium"
}
},
{
"id": 39,
"name": "IT/Data Experience",
"is_study_level": false,
"businessImpact": {
"id": 2,
"name": "Medium"
}
},
{
"id": 34,
"name": "Mgmt & Storage",
"is_study_level": false,
"businessImpact": {
"id": 3,
"name": "Minor"
}
}
],
"businessImpact": {
"id": 2,
"name": "Medium"
},
"sites_impacted": [],
"sites_available": []
},
And the property in question is businessImpact. As you can see it appears by itself as a property and inside array (and sometimes those arrays of arrays of their own).
I setup a function like:
arrayOfProperties.forEach((property) => {
obj[property].forEach((o) => {
o.businessImpact = newVal;
});
});
But of course it doesn't go deep enough.

JS Mapping object picking nested array key-values to make them higher level key-values

HHello. I have a mapping issue. I have nested data and i need to manupulate it to pick some values from nested array and make them higher level key-values. Here is the data i have and the data i want.
Data i have;
[
{
"id": 1,
"sku": "24-MB01",
"name": "Joust Duffle Bag",
"price": 34,
"custom_attributes": [
{
"attribute_code": "image",
"value": "/m/b/mb01-blue-0.jpg"
},
{
"attribute_code": "small_image",
"value": "/m/b/mb01-blue-0.jpg"
},
{
"attribute_code": "thumbnail",
"value": "/m/b/mb01-blue-0.jpg"
}
]
},
{
"id": 2,
"sku": "24-MB04",
"name": "Strive Shoulder Pack",
"price": 32,
"custom_attributes": [
{
"attribute_code": "small_image",
"value": "/m/b/mb04-black-0.jpg"
},
{
"attribute_code": "image",
"value": "/m/b/mb04-black-0.jpg"
},
{
"attribute_code": "description",
"value": "<p>Convenience is next to nothing when your day is crammed with action. So whether you're heading to class, gym, or the unbeaten path, make sure you've got your Strive Shoulder Pack stuffed with all your essentials, and extras as well.</p>\n<ul>\n<li>Zippered main compartment.</li>\n<li>Front zippered pocket.</li>\n<li>Side mesh pocket.</li>\n<li>Cell phone pocket on strap.</li>\n<li>Adjustable shoulder strap and top carry handle.</li>\n</ul>"
},
{
"attribute_code": "activity",
"value": "5438,5448,5450,5445"
}
]
}
]
The Data I want;
[
{
"id": 1,
"sku": "24-MB01",
"name": "Joust Duffle Bag",
"price": 34,
"image":"/m/b/mb01-blue-0.jpg"
},
{
"id": 2,
"sku": "24-MB04",
"name": "Strive Shoulder Pack",
"price": 32,
"image":"/m/b/mb04-black-0.jpg"
}
]
What i have so far;
var items = products.items.map(item => {
const custom_attr = item.custom_attributes.find(attr => !!attr.image) || {};
delete item.custom_attributes;
return {
...item,
...custom_attr
};
});
So basically i dont need the nested array, i just need the image(or maybe another attribute) data. But in the array all keys are the same(code and value as u see). I've tryed some mapping but couldn't get there. So i could use some help. Thanks in advance :)
In order to extract the image custom attribute, you have to find() the entry whose attribute_code is image:
const items = data.map(({ custom_attributes, ...item }) => {
const image = custom_attributes.find(
({ attribute_code }) => attribute_code === 'image'
)?.value;
return {
...item,
image,
};
});
Your code was pretty close. Instead of checking for !!attr, I assume what you meant to do was find the custom attribute with attribute: "image":
.find((attr) => attr.attribute_code === "image")
Additionally, instead of using delete (which will mutate the original object), you can use object destructuring and spread (...) to omit the custom_attributes property from the output object:
const products = {
items: [
{
id: 1,
sku: "24-MB01",
name: "Joust Duffle Bag",
price: 34,
custom_attributes: [
{
attribute_code: "image",
value: "/m/b/mb01-blue-0.jpg",
},
{
attribute_code: "small_image",
value: "/m/b/mb01-blue-0.jpg",
},
{
attribute_code: "thumbnail",
value: "/m/b/mb01-blue-0.jpg",
},
],
},
{
id: 2,
sku: "24-MB04",
name: "Strive Shoulder Pack",
price: 32,
custom_attributes: [
{
attribute_code: "small_image",
value: "/m/b/mb04-black-0.jpg",
},
{
attribute_code: "image",
value: "/m/b/mb04-black-0.jpg",
},
{
attribute_code: "description",
value:
"<p>Convenience is next to nothing when your day is crammed with action. So whether you're heading to class, gym, or the unbeaten path, make sure you've got your Strive Shoulder Pack stuffed with all your essentials, and extras as well.</p>\n<ul>\n<li>Zippered main compartment.</li>\n<li>Front zippered pocket.</li>\n<li>Side mesh pocket.</li>\n<li>Cell phone pocket on strap.</li>\n<li>Adjustable shoulder strap and top carry handle.</li>\n</ul>",
},
{
attribute_code: "activity",
value: "5438,5448,5450,5445",
},
],
},
],
};
const items = products.items.map(({ custom_attributes, ...item }) => {
const custom_attr =
custom_attributes.find((attr) => attr.attribute_code === "image") || {};
return {
...item,
...custom_attr,
};
});
console.log(items);

How to fetch data from object without using loop

This is the structure which is saved in the database. I want to fetch only "entityInfo" directly without using any loop.
let x = {
"12": [{
"entity": {
"id": "40ea18e6-f898-414a-96fd-b3ef5a0eb7cd"
},
"startTime": "2018-12-19T06:29:59.999Z"
},
{
"entity": {
"id": "40ea18e6-f898-414a-96fd-b3ef5a0eb7cd"
},
"startTime": "2018-12-19T06:49:25.000Z",
},
{
"entityInfo": [{
"entityName": "acd",
"timeSpent": 0.028055555555555556
}]
}
]
}
If you want the first item in the 12 array which has an entityInfo value, then you can use find
let x = {
"12": [{
"entity": {
"id": "40ea18e6-f898-414a-96fd-b3ef5a0eb7cd"
},
"startTime": "2018-12-19T06:29:59.999Z"
},
{
"entity": {
"id": "40ea18e6-f898-414a-96fd-b3ef5a0eb7cd"
},
"startTime": "2018-12-19T06:49:25.000Z",
},
{
"entityInfo": [{
"entityName": "acd",
"timeSpent": 0.028055555555555556
}]
}
]
}
console.log(x["12"].find(a => a.entityInfo))
You can use the map() function encapsulated. Click here.
But you have to question yourself if an array makes sense here.
let x = {
"12": [{
"entity": {
"id": "40ea18e6-f898-414a-96fd-b3ef5a0eb7cd"
},
"startTime": "2018-12-19T06:29:59.999Z"
},
{
"entity": {
"id": "40ea18e6-f898-414a-96fd-b3ef5a0eb7cd"
},
"startTime": "2018-12-19T06:49:25.000Z",
},
{
"entityInfo": [{
"entityName": "acd",
"timeSpent": 0.028055555555555556
}]
}
]
}
// will return a value if the entityInfo object exists
var ans = x["12"].filter((val)=>{return val.entityInfo})[0]
console.log(ans)
If there only is one entityInfo you can use the following to get the first element that has a "entityInfo" property.
x["12"].find(i => i.entityInfo)
Use Object.values for getting the values and then find for finding all the entity info objects
var x = {
"12": [{
"entity": {
"id": "40ea18e6-f898-414a-96fd-b3ef5a0eb7cd"
},
"startTime": "2018-12-19T06:29:59.999Z"
},
{
"entity": {
"id": "40ea18e6-f898-414a-96fd-b3ef5a0eb7cd"
},
"startTime": "2018-12-19T06:49:25.000Z",
},
{
"entityInfo": [{
"entityName": "acd",
"timeSpent": 0.028055555555555556
}]
}
]
};
console.log(Object.values(x).flat().find(el => el.entityInfo));
There are multiple approaches and possible solutions to this question.
You can use map, filter, reduce, find , forEach.
But each of the loop over elements internally, the other approach is if you know the position of entityInfo in x["12"] and you want to safe read it. Then you can use utilities like these.

Categories