how to create a javascript Set with objects? - javascript

I have a json file businessList.json with the following
[
{"availableTimes": [{"type": "time", "time": "06:30"}, {"type": "time", "time": "07:00"}]},
{"availableTimes": [{"type": "time", "time": "08:30"}, {"type": "time", "time": "07:00"}]}
]
In another file, I am trying to create a set out of this, but it won't work, it'll show all of the duplicate values. I'm assuming it's because of how objects are passed by reference. How could I solve this to get to the desired result?
const timesAvailable = new Set()
businessList.map(item => item.availableTimes.map(item => timesAvailable.add(item))) //won't work

Like the first comment by Pointy says, objects are only equal to each other in JavaScript if they refer to the same place in memory. For example,
const object1 = { foo: 'bar' };
const object2 = object1;
object1 === object2 //true;
{ foo: 'bar' } === { foo: 'bar' } //false
There isn't any way around it with Set. One thing I've done in a similar situation is loop through the array and create a dictionary (either with a JavaScript Object or Map), generating a unique key for each item, then iterating through that Object or Map to get the unique times.
For example, in your case, something like:
const availableTimesMap = availableTimes.reduce((acc, timeObject) => {
const key = `${timeObject.type}-${timeObject.time}`;
acc[key] = timeObject;
}, {});
const uniqueTimes = Object.values(availableTimesMap);
Hope this helps. Let me know if you have any questions.

I had a similar issue in my own project. To solve it I ended up having to use strings to be the keys of a Map object. You would need to figure out a standard way to ID a time so that two "identical" times are converted to identical strings. Simply using the time would probably work well enough.
const timesAvailable = new Map()
businessList.forEach(item =>
item.availableTimes.forEach(item =>
timesAvailable.add(item.time, item)
)
)
Alternatively, you could do away with objects and just use the string times.
[
{"availableTimes": ["06:30", "07:00"]},
{"availableTimes": ["08:30", "07:00"]}
]

I am not sure what the structure of the result you want to get. I assume that if you want to get a set of non-duplicate objects and each object looks like this: {"type": "time", "time": "06:30"}.
let businessList = [
{"availableTimes": [{"type": "time", "time": "06:30"}, {"type": "time", "time": "07:00"}]},
{"availableTimes": [{"type": "time", "time": "08:30"}, {"type": "time", "time": "07:00"}]}
]
const timesAvailable = new Set(); // a set of strings representing time, which is used for checking duplicates
const availableBusiness = new Set(); //the result we want
for(let business of businessList) {
for (let availableTime of business.availableTimes) {
if(availableTime.type === "time") {
if(!timesAvailable.has(availableTime.time)){
timesAvailable.add(availableTime.time);
availableBusiness.add(availableTime);
}
}
}
}
console.log(availableBusiness);
console.log(timesAvailable);

Related

How to "build" string form one property of an object array?

I have an array that contains objects, like this:
"exams": [
{
"id": "62b30836e941368db5d0e531",
"name": "Basic",
"price": 100
},
{
"id": "62b30836e941368db5d0e532",
"name": "Full",
"price": 200
}
]
I need pick only the name property from every object, and build a string, where the elements are "separated" with comma.
Like this:
"Basic,Full"
I tried the following, but the method built every whole object into the string:
var e = exams.join(",");
Before using the .join() to create a string, you should map the values you want from the array of objects. Like this:
exams.map(exam => exam.name)
This should return something like: ['Basic', 'Full'].
Then you do the join.
You could do all in one line (also avoid var, use const instead):
const e = exams.map(exam => exam.name).join(',')

how to convert array using filter method from values of another array in javascript

I've array like this
const data: { "status" : ["aa","bb","cc","dd"],
["ee","ff","gg","hh"],
["ii","jj","kk","ll"] }
const value=2;
and another array like this
const array2: { "name":"status", "data": ["cc","gg"] }
I want to convert array1 into this
const data: "status" : ["aa","bb","cc","dd"],
["ee","ff","gg","hh"]
here Value is the index where I get the data which I have to filter from second array.
and in array1, data has the key value pair where status is the key and value is the array
I use the filter method. but I'm not able to travel through status to filter the array.
P.S. thank you in advance
I'm not positive if this is what you are asking for, but it seems like you're wanting to trim the arrays of data.status to only the ones that contain at least one of the values in array2.data.
To check this, in your filter you can use the some array method and check if each element is included in array2.data.
const data = {
"status": [
["aa", "bb", "cc", "dd"],
["ee", "ff", "gg", "hh"],
["ii", "jj", "kk", "ll"]
]
}
const array2 = {
"name": "status",
"data": ["cc", "gg"]
}
console.log('Before')
console.log(data.status)
data.status = data.status.filter(row => row.some(elem => array2.data.includes(elem)))
console.log('After')
console.log(data.status)

Change property within deeply nested data structure where name matches

I have some data structure like so:
{
"category": "Fruits",
"department": "ABC123",
"content": {
"subcategory1": [
{
"fruitName": "Apples",
"inStock": false
},
{
"fruitName": "Pears",
"inStock": false
}
],
"subcategory2": [
{
"fruitName": "Oranges",
"inStock": false
},
{
"fruitName": "Lemons",
"inStock": false
}
]
}
}
I'd like to be able to update the "inStock" value based on some input. I have an array like:
["Pears", "Oranges"]
For all instances of the properties in that array, I want to update inStock to be true to end up:
{
"category": "Fruits",
"department": "ABC123",
"content": {
"subcategory1": [
{
"fruitName": "Apples",
"inStock": false
},
{
"fruitName": "Pears",
"inStock": true
}
],
"subcategory2": [
{
"fruitName": "Oranges",
"inStock": true
},
{
"fruitName": "Lemons",
"inStock": false
}
]
}
}
I'd like to write some generic function for that, so that I can do something like const newData = updateStock( oldData );. However I'm not sure how to start writing this. If I could get some pointers for starting (even if not a solution) I would be very thankful.
Please always post your attempt in OP so that we get to know how you are approaching..
You can try below approach using Array.filter and Array.forEach
let data = {
"category": "Fruits",
"department": "ABC123",
"content": {
"subcategory1": [
{
"fruitName": "Apples",
"inStock": false
},
{
"fruitName": "Pears",
"inStock": false
}
],
"subcategory2": [
{
"fruitName": "Oranges",
"inStock": false
},
{
"fruitName": "Lemons",
"inStock": false
}
]
}
}
function updateStock(names) {
Object.values(data.content).flat()
.filter(d => names.includes(d.fruitName))
.forEach(d => d.inStock = true)
}
updateStock(['Oranges', 'Pears'])
console.log(data)
I started writing this before I saw #NitishNarang's answer, so I'll finish and expand on it a bit.
Start by breaking your problem down into smaller pieces.
First, you want a way to update the inStock property on an object. We'll move on to updating multiple objects in a nested data structure later.
I'm going to take a functional approach here. Let's write a higher-order function (a function 'factory', so to speak). When called with a property name and a value, it will return a new function. This new function, when called with an object, will update that object with that property set to the specified value:
const updateProperty = (key, value) => obj => {
obj[key] = value;
return obj;
};
Note that this function can be used for all manner of things. We could make a function to update the key called material with the value chocolate if we wanted:
const renderUseless = updateProperty('material', 'chocolate');
Example:
const teapot = { material: 'china' };
renderUseless(teapot);
Anyway, we can use updateProperty to do more useful things, like create convenience functions for marking an item as in or out of stock:
const setInStock = updateProperty('inStock', true);
const setOutOfStock = updateProperty('inStock', false);
Now, how might we mark a bunch of things as being in stock?
const products = [
{
fruitName: 'Apples',
inStock: false
},
{
fruitName: 'Pears',
inStock: true
}
];
products.forEach(setInStock);
So how might we only run the function for those elements that match a given predicate? Well, we could use Array.filter, which when given an array, returns a new array containing only those items that passed a given predicate.
Let's build a function that accepts an array of fruit names, and returns a predicate that returns true if its input object has a fruitName property that is included in that array, and false otherwise:
const hasFruitName = names => item =>
names.includes(item.fruitName);
Used in conjunction with Array.filter:
const products = [
{
fruitName: 'Apples',
inStock: false
},
{
fruitName: 'Pears',
inStock: true
}
];
const matchingProducts = products.filter(isFruitName(['Apples']));
So we now have a solution for a flat array of items. We just need to deal with your nested data structure. How might we create a flat array of items, given that you have a nested object containing several arrays?
Object.values(data.content) will grab the values from the object data.content, yielding an array of arrays. To flatten this into a single array, we could use Array.flat, but it's not widely-supported yet. Let's make an equivalent portable function:
const flattenNestedArray = arr => [].concat.apply([], arr);
Now let's make another convenience function:
const getItemsFromData = data =>
flattenNestedArray(Object.values(data.content));
Putting it all together we get:
const setFruitInStock = (fruitNames, data) =>
getItemsFromData(data)
.filter(hasFruitName(fruitNames))
.forEach(setInStock);
This is a bit over-engineered, but hopefully it will illustrate the benefits of starting with small, generic, higher-order functions. You end up with a lot of small, reusable helpers, that grow into slightly more specialised building blocks. Everything is named clearly, and by the time you reach the final implementation it's obvious what's happening at a glance.
Finally, I added data as a second parameter so your function is a bit more reusable.
I'll leave refactoring this for purity (i.e. not mutating the input data) as an exercise for the OP :)

converting array to object while considering keys of the same value

I am trying to figure out an easy way to convert an array of objects to an object
I have an array of objects that looks like this:
[
{
"id": "-LP9_kAbqnsQwXq0oGDT",
"value": Object {
"date": 1541482236000,
"title": "First",
},
},
.... more objects here
]
And id like to convert it to an object with the timestamps as the keys, and arrays of objects corresponding to that date. If that key already exists, then add the object to the corresponding array associated with that key
{
1541482236000:
[{
"id": "-LP9_kAbqnsQwXq0oGDT",
"value": Object {
"date": 1541482236000,
"title": "First",
},
},
{
"id": "-LP9_kAbqnsQwXqZZZZ",
"value": Object {
"date": 1541482236000,
"title": "Some other title",
},
},
.... more objects here
],
1541482236001:
[{
"id": "-LP9_kAbqnsQ1234",
"value": Object {
"date": 1541482236001,
"title": "Another title",
},
},
.... more objects here
]
}
I was able to achieve something similar using reduce. However it does not handle adding objects to the array when their key already exists.
calendarReminders = action.value.reduce((obj, reminder) => {
dateKey = moment(reminder.value.date).format('YYYY-MM-DD')
obj[dateKey] = [reminder]
return obj;
}, {});
How can I do this?
You just need to check whether the object is already a key and if not add it with the value of an array. Then you can just push() into it:
let arr = [{"id": "-LP9_kAbqnsQwXq0oGDT","value": {"date": 1541482236000,"title": "First",},},{"id": "SomID","value": {"date": 1541482236000,"title": "Some other title",},},{"id": "A different ID","value": {"date": 1541482236001,"title": "A third title",},}]
let calendarReminders = arr.reduce((obj, reminder) => {
(obj[reminder.value.date] || (obj[reminder.value.date] = [])).push(reminder)
return obj;
}, {});
console.log(calendarReminders)
If you want to set the keys to a different format with moment, you should be able to do that without changing the basic idea.
Please test the below code!
First you iterate through your array of data,
if your result object/dictionary already has the key then you just add the current item
otherwise you make the key and set the value
const data = [];
let result = {};
for (const item of data) {
const key = item.value.date;
if (result.hasOwnProperty(key)) {
const prevData = result[key];
result[key] = [...prevData, item];
} else {
result[key] = [item];
}
}

How to create a new json out of three jsons?

I have 3 different jsons, I need to extrapolate some data from each and create a new json with it. The three jsons have an id identifier in common, a unique identifier, so We could use that as a match since they are actually three different big jsons.
On json one we have "id":"265", on two and three "article_id":"265", so these can be the reference point when we loop.
I never worked with json this way so I wouldn't know how to approach it. I have put jQuery and JS as tags as they're what I know best.
1
{
"id":"265",
"title":"Battle of Gettysburg",
"page_id":"4849",
"language_id":"en",
"original_time":"July 1\u20133, 1863"
}
2
{
"id":"185",
"original_name":"United States",
"country_id":"24",
"article_id":"265"
}
3
{
"id":"73",
"month":"July",
"year":"1863",
"suffix":"",
"article_id":"265"
}
So the end result I am looking for is a single json exactly like this, we take id and title as objects from json 1, then we grab original_name from json two and year object from json three and we'll have:
{
"id":"265",
"title":"Battle of Gettysburg",
"original_name":"United States",
"year":"1863"
}
NOTE
The json above are just examples, in reality they are three huge lists, what I could do (manually), is to join them in order to have a single json.
There is some terminology confusion here; based on your comments you could be asking one of two very different questions. Fortunately one of them is very simple to answer so let's do both.
(I am handwaving past the details of loading json strings into the browser and converting them into javascript objects.)
If you have three objects
...then this is just a matter of plucking out the fields you need individually when constructing an output object:
var in1 = {
"id": "265",
"title": "Battle of Gettysburg",
"page_id": "4849",
"language_id": "en",
"original_time": "July 1\u20133, 1863"
};
var in2 = {
"id": "185",
"original_name": "United States",
"country_id": "24",
"article_id": "265"
}
var in3 = {
"id": "73",
"month": "July",
"year": "1863",
"suffix": "",
"article_id": "265"
}
// construct a new object using the selected fields
// from each object in1, in2, or in3:
var out = {
id: in1.id,
title: in1.title,
original_name: in2.original_name,
year: in3.year
}
console.log(out);
If you have three lists of objects:
...in this case it's a lot more complicated (and a lot more interesting). In this case you would need to match fields from the objects in each list which share the same IDs.
The following is definitely not the most efficient or memory-conserving way to do this; I've spread things out to (hopefully) make it easier to follow what it's doing.
I'm making two assumptions:
within each list, all IDs are unique (meaning you won't have two objects with the same ID in one JSON file)
Every ID will appear in all three lists (meaning you don't need to handle missing fields in output)
/* Again handwaving past loading JSON strings and parsing
them into javascript objects, we'll just start with
three arrays: */
var input1 = [{
"id": "265",
"title": "Battle of Gettysburg",
"page_id": "4849",
"language_id": "en",
"original_time": "July 1\u20133, 1863"
},
{
"id": "1",
"title": "Foo",
"page_id": "123",
"language_id": "en",
"original_time": "July 1\u20133, 1863"
}
];
var input2 = [{
"id": "1",
"original_name": "Bar",
"country_id": "24",
"article_id": "265"
},
{
"id": "265",
"original_name": "United States",
"country_id": "24",
"article_id": "265"
}
]
var input3 = [{
"id": "1",
"month": "July",
"year": "Baz",
"suffix": "",
"article_id": "265"
},
{
"id": "265",
"month": "July",
"year": "1863",
"suffix": "",
"article_id": "265"
}
]
/* It would be much easier to find corresponding IDs
across these arrays if they weren't arrays. We'll
start by converting them into objects keyed by the
item ids: */
var convertArray = function(arr) {
var output = {};
arr.forEach(function(o) {
output[o.id] = o;
});
return output;
}
var obj1 = convertArray(input1);
var obj2 = convertArray(input2);
var obj3 = convertArray(input3);
/* Now if we need to find (say) the object with id "foo", we don't
need to search the whole array, but can just use `obj1["foo"]` or
`obj1.foo`.
The last step is to iterate over the list of IDs and repeatedly
do basically the same thing as in the "if you have three objects"
part above. The only difference is that we need to access the
object with the same ID in each of the input lists: */
var constructOutput = function(in1, in2, in3) {
var output = []; // we'll be outputting a list of objects again.
// step through every ID (assuming in1 contains all of them):
Object.keys(in1).forEach(function(id) {
var obj = {
id: id,
title: in1[id].title,
original_name: in2[id].original_name,
year: in3[id].year
}
output.push(obj);
});
return output;
}
var final = constructOutput(obj1, obj2, obj3)
console.log(final)
Essentially what you have to do is mimic a SQL JOIN using JavaScript objects:
Use JSON.parse() on all three JSON collections to turn them into arrays of objects.
Iterate through JSON 1 objects; for each object...
Iterate through JSON 2 objects, testing if article ID matches the ID from JSON 1 that we are iterating over. Save this object.
Iterate through JSON 3 objects, testing if ID matches the ID of the object we found from JSON 2. Save this object.
After you have all three objects, make a new object literal that contains only the fields you want:
{
Id: obj1.id,
Title: obj1.title,
Original_name: obj2.original_name,
Year: obj3.year
}
Should you want to combine n number of JSON objects, e.g. a list of objects you can take a functional approach and utilise reduce + filter.
const data = [{
"id":"265",
"title":"Battle of Gettysburg",
"page_id":"4849",
"language_id":"en",
"original_time":"July 1\u20133, 1863"
},
{
"id":"185",
"original_name":"United States",
"country_id":"24",
"article_id":"265"
},
{
"id":"73",
"month":"July",
"year":"1863",
"suffix":"",
"article_id":"265"
}];
const final = data.reduce((accu, { id, title }, index, array) => {
// Find any related objects
const matches = array.filter(data => data.article_id === id);
if (matches.length) {
// Flatten them for ease of access. Duplicate keys will override.
const flat = matches.reduce((arr, item) => ({ ...arr, ...item }), [])
// Return new object
return accu.concat({
...flat,
id,
title,
});
}
return accu;
}, []);
console.log(final, '<<')
// Witness
document.getElementById('results').innerHTML = JSON.stringify(final);
<div id="results" style="font-family: Courier; font-size 14px; color: #fff; background: #000; padding: 20px; max-width: 80vw;"></div>
Edited*
Maybe this is what you need?
let arrPages = [{
"id":"265",
"title":"Battle of Gettysburg",
"page_id":"4849",
"language_id":"en",
"original_time":"July 1\u20133, 1863"
}];
let arrArticles = [{
"id":"185",
"original_name":"United States",
"country_id":"24",
"article_id":"265"
},
{
"id":"73",
"month":"July",
"year":"1863",
"suffix":"",
"article_id":"265"
}];
let getResult = (arrInput, arrCompare) => {
let joinedItems = [];
arrInput.forEach(item => {
let newItem = { id: item.id, title: item.title };
arrCompare.forEach(subItem => {
if(subItem.article_id !== undefined && subItem.article_id === item.id){
if(subItem.original_name !== undefined)
newItem.original_name = subItem.original_name;
if(subItem.year !== undefined)
newItem.year = subItem.year;
}
});
joinedItems.push(newItem);
});
return joinedItems;
};
let result = getResult(arrPages, arrArticles);
console.log(result);
In the first part of the code i create a var that has the json data.
To solve the problema i create 2 functions, the order of the creation dosen't metter, the first function getJSONData() take the json data as parameter and return a object filtered by the keys defined in the array keys. The secound function just check if the current key is present in the array of keys, this function could be replaced by the jQuery.inArray() method.
// JSON data
var json = [{
"id":"265",
"title":"Battle of Gettysburg",
"page_id":"4849",
"language_id":"en",
"original_time":"July 1\u20133, 1863"
},
{
"id":"185",
"original_name":"United States",
"country_id":"24",
"article_id":"265"
},
{
"id":"73",
"month":"July",
"year":"1863",
"suffix":"",
"article_id":"265"
}]
// keys that i want
var keys = ["title", "original_name", "year"];
// var that will have the filtered data
var newJSON = getJSONData(json);
console.log(JSON.stringify(newJSON))
// this is the main function of the code
// here we iterate in the json creating a new object that has all the tags definid in the keys array
function getJSONData(arrayJSON){
var JSONFiltered = {};
for(var i in arrayJSON){
for(var key in arrayJSON[i]){
if(hasElement(key)){
JSONFiltered[key] = arrayJSON[i][key];
}
}
}
return JSONFiltered;
}
// this function is used to check a key is present in the array of keys
function hasElement(key){
for(var elem in keys){
if(keys[elem] == key) return true;
}
return false;
}

Categories