Pick one line to improve. Beginner efficiency code question - javascript

I was given homework on BigO Notation and we are supposed to improve a set of code for each exercise but I am having trouble on the final question. For this, they want us to improve the code efficiency by changing one line of code, two at most. When I run it through VSCode, it tells me that .values does not exist on object types so I am assuming to improve efficiency, it has something to do with that line but not sure what to change.
let myMovies = [
{
"title":"Rush Hour 2",
"year":2001,
"cast":[
"Jackie Chan",
"Chris Tucker"
],
"genres":[
"Comedy"
]
},
{
"title":"The Safety of Objects",
"year":2001,
"cast":[
"Glenn Close",
"Dermot Mulroney",
"Patricia Clarkson"
],
"genres":[
"Drama"
]
},
{
"title":"Rush Hour 2",
"year":2001,
"cast":[
"Jackie Chan",
"Chris Tucker"
],
"genres":[
"Comedy"
]
},
//etc...
]
function removeDuplicates(movies) {
let indexedMovies = {};
movies.forEach( (movie) => {
if (Object.keys(indexedMovies).indexOf(movie.title) < 0) {
indexedMovies[movie.title] = movie;
}
})
return indexedMovies.values();
}
let fixedMovies = removeDuplicates(myMovies);

I think the idea is that you would take this section of code:
movies.forEach( (movie) => {
if (Object.keys(indexedMovies).indexOf(movie.title) < 0) {
indexedMovies[movie.title] = movie;
}
})
And replace it with
movies.forEach( (movie) => {
indexedMovies[movie.title] = movie;
})
Because when you do indexedMovies[movie.title] it will replace any existing "duplicate", so there's no need to check for it explicitly with all the code in the if statement, which is pretty inefficient on its own -- creating an array just to linearly search for an item in that array.

1) There is no need to search for existing movie title as
Object.keys(indexedMovies).indexOf(movie.title) < 0
since it will take O(n) and increase the complexity, You can directly check for existence as(if you want to)
!indexedMovies[movie.title]
Any even you can eliminate this process and directly assign movie
indexedMovies[movie.title] = movie;
2) There is not such method on values() on indexedMovies object. Either return indexedMovies object or its values as
Object.values(indexedMovies)
let myMovies = [{
title: "Rush Hour 2",
year: 2001,
cast: ["Jackie Chan", "Chris Tucker"],
genres: ["Comedy"],
},
{
title: "The Safety of Objects",
year: 2001,
cast: ["Glenn Close", "Dermot Mulroney", "Patricia Clarkson"],
genres: ["Drama"],
},
{
title: "Rush Hour 2",
year: 2001,
cast: ["Jackie Chan", "Chris Tucker"],
genres: ["Comedy"],
},
//etc...
];
function removeDuplicates(movies) {
let indexedMovies = {};
movies.forEach((movie) => indexedMovies[movie.title] = movie);
return Object.values(indexedMovies);
}
let fixedMovies = removeDuplicates(myMovies);
console.log(fixedMovies);
/* This is not a part of answer. It is just to give the output fill height. So IGNORE IT */
.as-console-wrapper {
max-height: 100% !important;
top: 0;
}

Related

How to traverse through a tree like nested data structure of unknown depth in order to find and collect addressable array items?

Say I have an array that looks as such:
[{
"name": "Audiograms",
"folders": [{
"name": "2022"
}, {
"name": "2021"
}, {
"name": "2020"
}]
}, {
"name": "Patient Paperwork"
}, {
"name": "Repairs"
}]
And this array can have an infinite amount of objects and sub-objects, similar to a file tree.
I have an array letting me know the name of the folders I need to access from the root of the object, like:
["Audiograms", "2022"]
I also do not know this value ahead of time, nor do I know how many items are in this array ahead of time.
How would I be able to actually traverse this file tree using the array of names? I wish to do things like maybe pop the matching object out and move it to another part of the file tree.
Thank you!
OP
"I wish to do things like maybe pop the matching object out and move it to another part of the file tree."
In order to achieve follow-up tasks like the above mentioned one, the next provided solution walks the OP's folder structure and collects for each addressable match an object of two references, target and parent, where the former is the reference of the to be found folder-item, and the latter is the reference of its parent folder-item.
The solution got achieved by a recursively implemented reducer function.
function collectAddressableFolderRecursively(collector, folderItem) {
const { name = null, folders = [] } = folderItem;
const {
address: [parentName, childName], result,
} = collector;
if (name === parentName && folders.length) {
const targetFolder = folders
.find(({ name }) => name === childName) ?? null;
if (targetFolder !== null) {
result.push({
target: targetFolder,
parent: folderItem,
});
}
}
result.push(
...folders.reduce(collectAddressableFolderRecursively, {
address: [parentName, childName],
result: [],
})
.result
);
return collector;
}
const folders = [{
name: 'Audiograms',
folders: [{
name: '2022',
folders: [{
name: 'Audiograms',
folders: [{
name: '2022',
}, {
name: 'foo',
}],
}],
}, {
name: '2021',
}, {
name: '2020',
}]
}, {
name: 'Patient Paperwork',
}, {
name: 'Repairs',
folders: [{
name: 'Audiograms',
folders: [{
name: '2022',
}, {
name: 'bar',
}],
}, {
name: 'baz',
}],
}]
const address = ['Audiograms', '2022'];
const { result } = folders
.reduce(collectAddressableFolderRecursively, {
address,
result: [],
});
console.log({ address, result });
.as-console-wrapper { min-height: 100%!important; top: 0; }

Node: How can I filter on a JSON value that is nested within multiple arrays?

I am pulling data from an API that looks like this:
[{
id: "62ff4289163f2d1ec1d54ff16bd8d731",
sport_key: "americanfootball_ncaaf",
commence_time: "2021-08-28T17:00:00Z",
home_team: "Illinois Fighting Illini",
away_team: "Nebraska Cornhuskers",
bookmakers: [{
key: "unibet",
title: "Unibet",
last_update: "2021-07-16T23:33:36Z",
markets: [{
key: "spreads",
outcomes: [{
name: "Illinois Fighting Illini",
price: 1.89,
point: 8
},
{
name: "Nebraska Cornhuskers",
price: 1.89,
point: -8
}
]
}]
},
{
key: "barstool",
title: "Barstool Sportsbook",
last_update: "2021-07-16T23:28:36Z",
markets: [{
key: "spreads",
outcomes: [{
name: "Illinois Fighting Illini",
price: 1.91,
point: 8
},
{
name: "Nebraska Cornhuskers",
price: 1.91,
point: -8
}
]
}]
}
]
The relevant section of my code looks like this (the (data) is the JSON posted above):
response.on("end", () => {
const oddsData = JSON.parse(data);
let games = oddsData.length;
for(let i=0; i<games; i++){
let bookies = oddsData[i].bookmakers.length;
for(let b=0; b<bookies; b++){
console.log(oddsData[i].bookmakers[b]);
}
}
})
How can I filter to only show results for the bookmaker with the key "barstool"? I have been googling different array filters and reduce functions all week and I cannot get this figured out. Any help is appreciated.
As I can see you have an array which is containing first level objects and inside the first level objects you have bookmakers array which is an array of objects.
if you are looking to filter the bookmarks array, which is what I think what you should be looking for
dataArr.forEach(item => {
item.bookmakers = item.bookmakers.filter(bookmaker => bookmaker.key == "barstool")
})
if you are looking to filter the array containing first level objects, you can do something like below
dataArr = dataArr.filter(item => {
let keep = false;
item.bookmakers.forEach(bookmaker => {
if(bookmaker.key == "barstool"){
keep = true;
}
})
return keep;
})
I would guess something like that:
gamesWithBarstool = oddsData.filter(game => game.bookmakers.some(bookmaker => bookmaker.key === 'barstool'))
With that you should only get the list of games that have odds for your named bookmaker.
filter:
Will return the subset of the array that matches the test.
some:
Will return true if any item in the array matches the test.

Dynamically parsing JSON arrays in JavaScript

I'm trying to parse a JSON file stored locally on my machine in JavaScript in discord.js (v12). This JSON has several keys and values:
{
"name": "Robert",
"rank": "Owner",
"hobbies": [{
"id": 1,
"name": "gaming"
}, {
"id": 2,
"name": "listening to music"
}, {
"id": 3,
"name": "vibing"
}, {
"id": 4,
"name": "driving down the highway"
}],
"roles": [{
"id": 1,
"name": "Founder"
}, {
"id": 2,
"name": "Premium Member"
}]
}
I want to send the above in a message on Discord as follows:
name: Robert
rank: Owner
hobbies: gaming, listening to music, vibing, driving down the highway
roles: Founder, Premium Member
I also want this to be dynamic. Meaning my code should adapt if a new key and value is added to the current set.
With the current code used, this is my result:
name: Robert
rank: Owner
hobbies: gaming, listening to music, vibing, driving down the highway
This is my current code:
let noted = ``
var raw = fs.readFileSync(name)
var obj = JSON.parse(raw)
for (var item in obj) {
if (obj[item] instanceof Object) {
for (var i in obj.hobbies) {
noted += `${obj.hobbies[i].name}, `
}
} else {
noted += `${item}: ${obj[item]}\n`
noted += `hobbies: `
}
}
message.channel.send(noted)
The variable name is const name = require("./names.json"); at the top of the code.
This code works fine with name, rank and hobbies.
roles has to be manually checked in the for loop if I want it to be visible. My goal is to cause any new keys to be added to be automatically detected and added into the noted variable.
I've seen something similar done using map(), but I tried it without getting anywhere good. This is rather sloppy code as well but I'm not interested in keeping it clean.
You could do something like this with map and join:
const obj = {"name":"Robert","rank":"Owner","hobbies":[{"id":1,"name":"gaming"},{"id":2,"name":"listening to music"},{"id":3,"name":"vibing"},{"id":4,"name":"driving down the highway"}],"roles":[{"id":1,"name":"Founder"},{"id":2,"name":"Premium Member"}]};
const noted = Object.entries(obj)
.map(([key, val]) =>
`${key}: ${
val instanceof Array ? val.map(x => x.name).join(', ') : val
}`)
.join('\n');
console.log(noted);
Here is an iterative solution using object-scan.
I find it a bit easier to read, but most importantly it is very flexible as to which keys you want to traverse.
// const objectScan = require('object-scan');
const myData = { name: 'Robert', rank: 'Owner', hobbies: [{ id: 1, name: 'gaming' }, { id: 2, name: 'listening to music' }, { id: 3, name: 'vibing' }, { id: 4, name: 'driving down the highway' }], roles: [{ id: 1, name: 'Founder' }, { id: 2, name: 'Premium Member' }] };
const convert = (data) => {
const r = objectScan(['*', '*[*].name'], {
reverse: false,
filterFn: ({ isLeaf, context, key, value }) => {
if (isLeaf) {
if (!(key[0] in context)) {
context[key[0]] = value;
} else {
context[key[0]] += `, ${value}`;
}
}
}
})(data, {});
return Object.entries(r).map(([k, v]) => `${k}: ${v}`).join('\n');
};
console.log(convert(myData));
/* =>
name: Robert
rank: Owner
hobbies: gaming, listening to music, vibing, driving down the highway
roles: Founder, Premium Member
*/
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan#13.8.0"></script>
Disclaimer: I'm the author of object-scan

Looping through nested data and displaying object properties and values

In my React app, I'm looking for a clean way to loop through the following dynamic data structure and display the object properties and values.
Sample data:
data: {
company: [
{
company_name: "XYZ Firm",
company_email: "hello#xyz.com",
company_phone: 91982712,
}
],
shareholders: [
{
shareholder_name: "Lin",
percentage: 45
},
{
shareholder_name: "Alex",
percentage: 10
},
],
employees: [
{
employee_name: "May",
employee_email: "may#xyz.com"
},
]
}
The output I want is:
company_name: XYZ Firm
company_email: hello#xyz.com
company_phone: 91982712
shareholder_name: Lin
shareholder_percentage: 45
shareholder_name: Alex
shareholder_percentage: 10
employee_name: May
employee_email: may#xyz.com
This is what I've tried so far:
//data contains the entire object
const profileInfo = Object.keys(data).map(key => {
let profileSection = [];
for (let values of data[key]) { //retrieve the objects of each "section" e.g., company, shareholders
Object.keys(values).map(key => {
profileSection.push(<p>{key}: {values[key]}</p>);
})
}
return profileSection;
})
I'm able to achieve the intended results but I'm not sure if it's the best solution in terms of performance. Having nested Object.keys().mapseems a bit off to me.
Note: User will be able to add more shareholders/employees.
Here is a somewhat shorter version using Object.values() and Object.entries().
var data = { company: [ { company_name: "XYZ Firm", company_email: "hello#xyz.com", company_phone: 91982712, } ], shareholders: [ { shareholder_name: "Lin", percentage: 45 }, { shareholder_name: "Alex", percentage: 10 }, ], employees: [ { employee_name: "May", employee_email: "may#xyz.com" }, ] };
let profileInfo = [];
Object.values(data).flat().forEach((item) => {
Object.entries(item).forEach(([key, value]) => {
profileInfo.push(key + ": " + value);
});
});
console.log(profileInfo);

How to omit values while using groupby

I have an array of JSON objects like the following:
{ BlockId: '979',
Day: 'Preliminary/Final Qualifying Day 1',
Event: 'Preliminary Qualifying',
FirstName: 'some',
LastName: 'one',
PlayerPosition: '6',
TimeSlot: '4/21/2018 12:00:00 PM'
}
I wanted to group the objects based on values and did the following:
var result = _(json)
.groupBy('Day')
.mapValues(function(groupedByDay) {
return _(groupedByDay)
.groupBy('Event')
.mapValues(function (groupedByDayAndEvent) {
return _.groupBy(groupedByDayAndEvent, 'BlockId');
})
.value();
})
.value();
Which gave me the following:
{"Preliminary/Final Qualifying Day 1":
{"Preliminary Qualifying":
{ "977":[{"BlockId":"977",
"Day":"Preliminary/Final Qualifying Day 1",
"Event":"Preliminary Qualifying",
"FirstName":"some",
"LastName":"one",
"PlayerPosition":"0",
"TimeSlot":"4/21/2018 9:00:00 AM"
}]
}
}
}
I'd like to remove the fields that I grouped by. Namely: BlockId, Day and Event. Any ideas on how I could omit these values with the code I presented? I'm lost :(
EDIT:
It seems that I forgot that _.omit creates a copy of the object without the fields given... I came to this solution that I don't believe is efficient at all. Any suggestions to make it better?
for(var day in result) {
for(var event in result[day]) {
for(var block in result[day][event]) {
for(var item in result[day][event][block]) {
delete result[day][event][block][item].BlockId;
delete result[day][event][block][item].Day;
delete result[day][event][block][item].Event;
}
}
}
}
use _omit in the deepest level
let myArr = [{
"BlockId": "979",
"Day": "Preliminary/Final Qualifying Day 1",
"Event": "Preliminary Qualifying",
"FirstName": "Robert",
"LastName": "Oristaglio",
"PlayerPosition": "6",
"TimeSlot": "4/21/2018 12:00:00 PM"
}]
var result = _(myArr)
.groupBy('Day')
.mapValues(function(groupedByDay) {
return _(groupedByDay)
.groupBy('Event')
.mapValues(function(groupedByDayAndEvent) {
return _(groupedByDayAndEvent)
.groupBy('BlockId')
.mapValues(function(groupedByAll) {
return groupedByAll.map(function(entry) {
return _.omit(entry, ['Day', 'Event', 'BlockId']);
})
})
.value();
})
.value();
})
.value();
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.min.js"></script>

Categories

Resources