How to change an object Key without mutating original array - javascript

I have an array of objects - I want to change one of the object keys to something else without mutating the original array. what is the best method to approach this?
I understand that I could be using the Map method but unsure how this would work. thanks
const books = [
{ title: "To Kill a Mockingbird", writtenBy: "Harper Lee" },
{ title: "A Clockwork Orange", author: "Anthony Burgess" },
{ title: "The Elephant Tree", writtenBy: "R.D. Ronald" }
]
function changeKey(arr, keyChange, newKey) {
}
// i want to return so the KEY keyChange(author) is changed to newKey(writtenBy)
[
{ title: "To Kill a Mockingbird", writtenBy: "Harper Lee" },
{ title: "A Clockwork Orange", writtenBy: "Anthony Burgess" },
{ title: "The Elephant Tree", writtenBy: "R.D. Ronald" }
]

You can map the parameter array and copy each of the objects within it shallowly using the spread operator. For each new object, if it contains the key we'd like to remove, copy the value to the new key and delete the old key.
const books = [ {title: "To Kill a Mockingbird", writtenBy: "Harper Lee"}, {title: "A Clockwork Orange", author: "Anthony Burgess"}, {title: "The Elephant Tree", writtenBy: "R.D. Ronald"} ];
const changeKey = (arr, keyChange, newKey) =>
arr.map(e => {
const o = {...e};
if (keyChange in o) {
o[newKey] = o[keyChange];
delete o[keyChange];
}
return o;
})
;
console.log(changeKey(books, "author", "writtenBy"));
console.log(books);

Array helpers like map, filter, reduce, etc doesn't mutate the original array they return a new array. Map receives a function as an argument (callback). Map iterate the array applying your callback in every element.
const books = [ {title: "To Kill a Mockingbird", writtenBy: "Harper Lee"},
{title: "A Clockwork Orange", author: "Anthony Burgess"},
{title: "The Elephant Tree", writtenBy: "R.D. Ronald"} ];
//Function to use as a callback on map
function changeKey(current) {
if(current.author) return { title: current.title, writtenBy: current.author };
return current;
}
//Creating new array applying changeKey in every element thanks to map
const newBooks = books.map(changeKey);
console.log(newBooks);

The following will not mutate books array.
const books = [
{ title: "To Kill a Mockingbird", writtenBy: "Harper Lee" },
{ title: "A Clockwork Orange", author: "Anthony Burgess" },
{ title: "The Elephant Tree", writtenBy: "R.D. Ronald" }
];
const renamedBooks = books.map(book => {
if (book.author) {
return {
title: book.title,
writtenBy: book.author
};
}
return book;
});
console.info(renamedBooks);

Related

How to do a join from 2 JSON by a common ID field in Javascript

I have two JSON files: JSON A has some company properties and the company_id, while JSON B has company names and company ids.
JSON A example:
[
{
"order_name": "Foo",
"company_id": "112233"
},
{
"order_name": "Bar",
"company_id": "123456"
}
]
JSONB example:
[
{
"company_id":"112233",
"name":"ACME company",
},
{
"company_id":"123456",
"name":"John Doe Inc.",
}
]
Which is the most efficient way to do a join by the company_id values? I would like to have the JSON C (merged result) with the company names correctly added, like this:
[
{
"order_name": "Foo",
"company_id": "123456",
"company_name": "John Doe Inc."
},
{
"order_name": "Bar",
"company_id": "112233",
"company_name": "ACME company"
}
]
Is looping and filter for each the only solution? Is there a more efficient way to do this from a performance point of view?
More info:
JSON is not sorted by company_id.
Array A could have more than one object with the same company_id
I'm using Javascript (in a Vue.js app), I don't need to support old browsers
In common modern JavaScript, you can do this as you mentioned with higher-order functions like map, filter, and so on:
const arrayA = [
{
"order_name": "Foo",
"company_id": "112233"
},
{
"order_name": "Bar",
"company_id": "123456"
}
]
const arrayB = [
{
"company_id":"112233",
"name":"ACME company",
},
{
"company_id":"123456",
"name":"John Doe Inc.",
}
]
const mergeAB = arrayA.map( companyA => {
const matched = arrayB.find(companyB => companyB.company_id === companyA.company_id)
if(matched) {
return {...companyA, ...matched}
} else {
// return companyA element or customize it with your case
}
}
)
console.log(mergeAB)
Note 1: Array.find() method complexity is O(n) and Array.map() method complexity is O(n)
Note 2: efficiency is an important thing but not in all situations. sometimes you need to do these types of iteration one time or for a small array size, so no need to worry about the performance.
Note 3: you could compare the answer and find out your best solution since we don't know about your whole code and application.
I hope this will work for you. Let me know if you have any questions.
const arrayOne = [
{
"order_name": "Foo",
"company_id": "112233"
},
{
"order_name": "Bar",
"company_id": "123456"
}
];
const arrayTwo = [
{
"company_id":"112233",
"name":"ACME company",
},
{
"company_id":"123456",
"name":"John Doe Inc.",
}
];
const [source, target] = arrayOne.length > arrayTwo.length
? [arrayOne, arrayTwo]
: [arrayTwo, arrayOne];
const merged = source.map(object =>
{
// Assuming that in the 2nd array, the match is only found 1 time and it EXISTS.
const matched = target.find(element => element.company_id === object.company_id);
// Merge both objects together
return {
...object,
...matched
};
});
console.log(merged);
By having JSONs:
const jsonA = [
{
"order_name": "Foo",
"company_id": "112233"
},
{
"order_name": "Bar",
"company_id": "123456"
}
];
const jsonB = [
{
"company_id":"112233",
"name":"ACME company",
},
{
"company_id":"123456",
"name":"John Doe Inc.",
}
];
you can merge maps into 3rd map with something like this:
const transform = (data, current={}) =>
data.reduce((prev, company) => {
if(!prev[company['company_id']]) prev[company['company_id']] = {};
prev[company['company_id']] = {...prev[company['company_id']], ...company}
return prev;
}, current);
let jsonMap = transform(jsonA, {});
jsonMap = transform(jsonB, jsonMap);
let jsonC = Object.keys(jsonMap).map(companyId => jsonMap[companyId] );
console.log(jsonC);

Remove objects from array comparing with another array

I have an array blackList where I store blacklisted business names, now I have a results array of objects with many business, I want to store in an array the business names which are not included in the blackListed array, what is the easier and most performant way to do this?
Is a nested loop really needed for this?
blackList = [ "Dominos Pizza", "Domino's Pizza", "McDonald's", "McDonalds", "Telepizza", "Subway", "Burger King", "KFC", "Pans&Co", "Pans&Company" ,
"Rodilla", "Rodilla Campamento", "Granier", "Llaollao" , "Taco Bell", "Wendy's", "Dunkin' Donuts", "Pizza Hut", "Papa John's Pizza", "Little Caesars",
"Panera Bread", "Chipotle", "Papa Murphy's", "Hungry Howie", "Chipotle Mexican Grill", "Starbucks"],
list = [ { name:'business 1' }, { name:'business 2' }, { name:'business 3' } ]
The easiest way to do this is to use filter and destructuring:
const blackList = ["Dominos Pizza", "Domino's Pizza", "McDonald's", "McDonalds", "Telepizza", "Subway", "Burger King", "KFC", "Pans&Co", "Pans&Company",
"Rodilla", "Rodilla Campamento", "Granier", "Llaollao", "Taco Bell", "Wendy's", "Dunkin' Donuts", "Pizza Hut", "Papa John's Pizza", "Little Caesars",
"Panera Bread", "Chipotle", "Papa Murphy's", "Hungry Howie", "Chipotle Mexican Grill", "Starbucks"
];
const list = [{
name: 'business 1'
}, {
name: 'business 2'
}, {
name: 'business 3'
}, {
name: "Granier"
}];
const notOnBlacklist = list.filter(({ name }) => !blackList.includes(name));
console.log(notOnBlacklist);
.as-console-wrapper { max-height: 100% !important; top: auto; }
Try
let blackList =
{"Dominos Pizza":1, "Domino's Pizza":1, "McDonald's":1, "McDonalds":1
,"Telepizza":1, "Subway":1, "Burger King":1, "KFC":1, "Pans&Co":1
,"Pans&Company":1, "Rodilla":1, "Rodilla Campamento":1, "Granier":1
,"Llaollao":1, "Taco Bell":1, "Wendy's":1, "Dunkin' Donuts":1, "Pizza Hut":1
,"Papa John's Pizza":1, "Little Caesars":1, "Panera Bread":1, "Chipotle":1
,"Papa Murphy's":1, "Hungry Howie":1, "Chipotle Mexican Grill":1
,"Starbucks":1 };
let list = [];
addBusiness("'business 1'");
addBusiness("Domino's Pizza");
addBusiness("'business 2'");
addBusiness("Hungry Howie");
addBusiness("'business 3'");
console.log(list);
function addBusiness (name) {
if (blackList.hasOwnProperty(name)) return;
list.push({'name':name});
}

Looping over Array of objects and returning random object value - JavaScript

I am trying to loop over an array of objects and display a random quote from the data, so far my code returns undefined. Any idea's why?
Code so far..
const quotesToUse = [{
quote: "This was the biggest quote ever - Array Object",
author: "Derek Bullshido",
},
{
quote: "Hey hows it going - Array Object",
author: "Paul Frank",
},
{
quote: "Why can I say that - Array Object",
author: "Derek Paul",
},
]
function randomQuotes() {
for (var i = 0; i < 1; i += 1 ) {
const randomQuote = quotesToUse[i][Math.floor(Math.random() * quotesToUse[i].quote.length)];
document.getElementById("container").appendChild(text).append(randomQuote);
}
I am trying to display the quote (String) randomly.
You probably want this to get the quote:
const randomQuote = quotesToUse[Math.floor(Math.random() * quotesToUse.length)].quote;
I don't know what you're trying to do with the loop.
You can return random quotes with this function:
const quotesToUse = [{
quote: "This was the biggest quote ever - Array Object",
author: "Derek Bullshido",
},
{
quote: "Hey hows it going - Array Object",
author: "Paul Frank",
},
{
quote: "Why can I say that - Array Object",
author: "Derek Paul",
},
]
function randomQuotes() {
const randomQuote = quotesToUse[Math.floor(Math.random() * quotesToUse.length)];
const quote = document.createElement("DIV");
quote.textContent=randomQuote.quote;
const author = document.createElement("DIV");
author.textContent=randomQuote.author; document.getElementById("container").appendChild(quote).appendChild(author);
}
randomQuotes();
<div id="container"></div>
You can do this more readably with rando.js. Just don't forget to add the script tag to the head of your HTML document if you want to use this code.
const quotesToUse = [
{quote: "This was the biggest quote ever - Array Object", author: "Derek Bullshido"},
{quote: "Hey hows it going - Array Object", author: "Paul Frank"},
{quote: "Why can I say that - Array Object", author: "Derek Paul"},
];
var randomValueFromArray = rando(quotesToUse).value;
console.log(randomValueFromArray.quote);
<script src="https://randojs.com/1.0.0.js"></script>

Remove objects from an array where their properties are not found in another array

Given an array of Special objects as follows:
var allSpecials = []; //Yet to be populated
When populated, each special object in the array looks like this,
e.g.
{address: "250 Manukau Road", category: "Breakfast Special", coords: so, cusine_type: "Japanese", establishment_type: "Restaurant", …}
{address: "557 Manukau Rd", category: "Dinner Special", coords: so, cusine_type: "Italian", establishment_type: "Bar", …}
I want to remove for example, element 0 in the allSpecials array as it's category, "Breakfast Special", is not found in the following array
specialCategories = ["Happy Hour", "Dinner Special", "Lunch Special"];
How can I go about this?
Many thanks
You can use lodash keyby, pick and values functions.
const categorySpecialMap = keyBy(allSpecials, 'category');
const targetSpecials = values(pick(categorySpecialMap, specialCategories));
I believe what you are trying to do is to remove elements whose categories are not found in the specialCategories array?
Given an array:
const allSpecial = [{address: "250 Manukau Road", category: "Breakfast Special", coords: "so", cusine_type: "Japanese", establishment_type: "Restaurant"}, {address: "557 Manukau Rd", category: "Dinner Special", coords: "so", cusine_type: "Italian", establishment_type: "Bar"}, {address: "333", category: "x"}]
and another array:
const specialCategories = ["Breakfast Special", "Happy Hour", "Dinner Special", "Lunch Special"];
You can filter the allSpecials array where the category isn't included in the specialCategories array like so:
allSpecials.filter(x => specialCategories.includes(x.category))
OR
If you want to dynamically populate specialCategories with only objects that match, you can do:
myArrayOfObjects.forEach(e => {
if (specialCategories.includes(e.category)) {
allSpecials.push(e)
}
})

Create complex array from list

If I have an array with many items similar to this:
[
["Core", "Mathematics", "Mathematics 20-4"],
["Core", "Mathematics", "Mathematics 30-1"],
["Other", "Fine Arts", "Art", "some art course"],
["Other", "Fine Arts", "Music", "some music course"],
["Other", "Forensics", "some forensics course"],
["French Immersion", "Core", "Mathématiques", "Mathématiques 30-1"]
]
Where the structure is essentially "Department -> Subject -> Course".
I want to dynamically create an Array (or Object) similar to the following (or whatever makes the most sense)...
{
subjects: [
{
title: "Mathematics", courses: [ "Mathematics 20-4", "Mathematics 30-1" ]
},
{
title: "Mathématiques", lang: "fr", courses: [ "Mathématiques 30-1" ]
}
],
other: {
subjects: [
{
title: "Forensics", courses: [ "some forensics course" ]
},
{
title: "Fine Arts", subjects: [
{
title: "Art", courses: [ "some art course" ]
},
{
title: "Music", courses: [ "some music course" ]
}
]
}
]
}
}
The "Other" department doesn't necessarily follow "Subject -> Course" and rather can have "Subject -> Subject -> Course" and "Subject -> Course". Maybe adding a type="course" and type="subject" might help, but I'd still like it to have a heirarchy.
I've been banging my head over how to dynamically convert this into an Array or Object structure.
var courses = {};
for(var i =0; i<arr.length; i++){
var department = arr[i][0];
var subject = arr[i][1];
var course = arr[i][2];
courses[department]= courses[department] || {};
courses[department][subject] = courses[department][subject] || [];
courses[department][subject].push(course);
}
That will generate an object in the form
courses = {
core:{
mathematics:["math1","math2"],
english: ["english1,"english2"]
}
Other:{
"Fine Arts":[...],
"Forensics":[...]
}
}
Which I think is what you want.
Then if you want an array of courses for a specific subject for example, you can access it with
var courselist = courses[<department>][<subject];
Using inspiration from #ben336, #user1787152 and also DevShed forum thread I came up with the following code:
var Department,
departments = [];
Department = function(title) {
this.title = title;
this.subjects = [];
};
function parseTitles( titles )
{
var i, department, departmentTitle,
hasDepartment = false;
departmentTitle = titles.shift();
for (i=0; i<departments.length; i++) {
if (departments[i].title === departmentTitle) {
hasDepartment = true;
break;
}
}
if (!hasDepartment) {
department = new Department(departmentTitle);
departments.push(department);
}
departments[i].subjects = titles;
}
The subjects are being used as a form of navigation, with the courses being queried via JSON. I'm keeping the subjects as an Array, where when the last child in the subject Array is clicked it will query the JSON for the courses for the subject.
I'll see if I can give credit to #ben336 as he posted the only answer and I'd like to give some credit.

Categories