Does angularjs automatically create nested structures? - javascript

a bit confused about how to create and reference items in a nested array using angular.js. I was thinking that I could do:
$scope.zones = [ {z:'north'}, {z:'south'} ];
$scope.zones.times = [ {t:'noon'}, {t:'midnight'} ];
$scope.zones.times.places = [ {p:'here'}, {p:'there'} ];
and angularjs would create a structure in which every zone has two times and every time has two places.
Then I could use something like:
<ul ng-repeat="zone in $scope.zones">
<li>{{zone.z}}</li>
<ul ng-repeat="time in zone.times">
<li>{{time.t}}</li>
<ul ng-repeat="place in time.places">
<li>{{place.p}}</li>
</ul>
</ul>
</ul>
to see the tree structure on my page.
So, does using the dot notation above actually create a nested array of objects? Should I be able to reference them "recursively" as in the directive above? I'm having trouble getting this to work beyond the first two levels.

You're not setting up your data correctly. It should be:
$scope.zones = [ {z:'north'}, {z:'south'} ];
$scope.zones[0].times = [ {t:'noon'}, {t:'midnight'} ];
$scope.zones[1].times = [ {t:'noon'}, {t:'midnight'} ];
Etc...
Then your HTML should work as expected.

In JavaScript using obj.propName accesses the property named propName of the object referenced by obj.
Arrays are also objects (in JavaScript), so these lines of code:
$scope.zones = [...];
$scope.zones.times = [...];
create an array (named zones) and give it a property named times (which is also an array).
Note: This is JavaScript-specific and has nothing to do with Angular.
This is not what you want. You want to give the times property to zones' items, not to zones itself. (Similarly with places.)
In order to achieve that, you need to iterate over the elements of each array and give it the extra properties.
var places = [{p: 'here'}, {p: 'there'}];
var times = [{t: 'noon'}, {t: 'midnight'}];
var zones = [{z: 'north'}, {z: 'south'}];
// Give each `time` a `places` property
times.forEach(function (time) {
time.places = places;
});
// Give each `zone` a `times` property
// (Each `time` already has a `places` property.)
zones.forEach(function (zone) {
zone.times = times;
});
// Assign the augmented `zones` to `$scope.zones`
$scope.zones = zones;
See, also, this short demo.
Note:
In the above implementation, values are passed by reference. This means that is you change $scope.zones[0].times[0].places[0], $scope.zones[1].times[1].places[0] will also be affected, since it references the same object.
This implementation is OK if you just want to read values, as it's more efficient.
If you want to be able to also modify the objects, then you need to create copies of the objects and not assign them by reference.
E.g., instead of time.places = places; and zone.times = times;
write time.places = angular.copy(places); and zone.times = angular.copy(times); respectively.
The resulting $scope.zones object will look liek this:
[{
"z": "north",
"times": [{
"t": "noon",
"places": [
{"p": "here" },
{"p": "there"}
]
}, {
"t": "midnight",
"places": [
{"p": "here" },
{"p": "there"}
]
}]
}, {
"z": "south",
"times": [{
"t": "noon",
"places": [
{"p": "here" },
{"p": "there"}
]
}, {
"t": "midnight",
"places": [
{"p":"here" },
{"p":"there"}
]
}]
}]
Note:
In your HTML code you reference $scope.zones. This is an error !
The corect way is: zones
(All Angular expressions are evaluated in the context of the current Scope, thus ng-repeat="zone in $scope.zones" will look for $scope.$scope.zones.)

Related

Finding like values and appending items to array (javascript)

I have two arrays I'm trying to combine in a very specific way and I need a little guidance. Array1 is an array of dates 30-40 items, Array 2 is a list of objects with a date inside one of the attributes. I'm trying to append the object in array 2 to the index of array1 when the dates match.
I want to put arr2 in the same index as arr1 if the dates match.
const arr = [
"2022-06-26T07:00:00.000Z",
"2022-06-27T07:00:00.000Z",
"2022-06-28T07:00:00.000Z",
"2022-06-29T07:00:00.000Z",
"2022-06-30T07:00:00.000Z",
"2022-07-01T07:00:00.000Z",
"2022-07-02T07:00:00.000Z",
"2022-07-03T07:00:00.000Z",
"2022-07-04T07:00:00.000Z",
"2022-07-05T07:00:00.000Z",
"2022-07-06T07:00:00.000Z",
"2022-07-07T07:00:00.000Z",
"2022-07-08T07:00:00.000Z",
"2022-07-09T07:00:00.000Z",
"2022-07-10T07:00:00.000Z",
"2022-07-11T07:00:00.000Z",
"2022-07-12T07:00:00.000Z",
"2022-07-13T07:00:00.000Z",
"2022-07-14T07:00:00.000Z",
"2022-07-15T07:00:00.000Z",
"2022-07-16T07:00:00.000Z",
"2022-07-17T07:00:00.000Z",
"2022-07-18T07:00:00.000Z",
"2022-07-19T07:00:00.000Z",
"2022-07-20T07:00:00.000Z",
"2022-07-21T07:00:00.000Z",
"2022-07-22T07:00:00.000Z",
"2022-07-23T07:00:00.000Z",
"2022-07-24T07:00:00.000Z",
"2022-07-25T07:00:00.000Z",
"2022-07-26T07:00:00.000Z",
"2022-07-27T07:00:00.000Z",
"2022-07-28T07:00:00.000Z",
"2022-07-29T07:00:00.000Z",
"2022-07-30T07:00:00.000Z",
"2022-07-31T07:00:00.000Z",
"2022-08-01T07:00:00.000Z",
"2022-08-02T07:00:00.000Z",
"2022-08-03T07:00:00.000Z",
"2022-08-04T07:00:00.000Z",
"2022-08-05T07:00:00.000Z",
"2022-08-06T07:00:00.000Z"
]
const arr2 = [
{
"gsi1SK": "name ",
"searchPK": "thing",
"SK": "uuid",
"Desc": "place #1205",
"PK": "thing uuid",
"searchSK": "7/1/2022",
"gsi1PK": "thing",
"Complete": false,
"Users": [
"person1",
"person2"
]
},
{
"gsi1SK": "name",
"searchPK": "thing",
"SK": "uuid",
"Desc": "place#124124",
"PK": "thing uuid",
"searchSK": "7/4/2022",
"gsi1PK": "thing",
"Complete": false,
"Users": [
"person2",
"person45"
]
}
]
console.log([arr, arr2]);
You seem to have a handle on the date conversion part. Here I've defined two short sample arrays to represent arr2 and newArr. Then, a map function to create the output.
const arr2 = [
{
"OTHER_FIELDS": "TOP SECRET",
"searchSK":"7/4/2022"
},
{
"OTHER_FIELDS": "TOP SECRET",
"searchSK":"7/9/2022"
}
];
const newArr = [
[
"7/2/2022"
],
[
"7/3/2022"
],
[
"7/4/2022"
],
[
"7/5/2022"
],
[
"7/6/2022"
],
[
"7/7/2022"
],
[
"7/8/2022"
],
[
"7/9/2022"
],
[
"7/10/2022"
]
];
// for each subarray in newArr, return an array containing the existing element plus any elements from arr2 found by the filter function
const output = newArr.map(el => [...el, ...arr2.filter(a2 => a2.searchSK === el[0])]);
console.log(output);
Plan
You've got two obvious options:
A. Look at each of the objects, finding a home for each one in turn
B. Look at each of the dates, collecting all the objects that belong to it
Which method makes more sense for you will depend on other factors you haven't covered in your post. I think the main question is: is it guaranteed that the date list will contain a proper home for every object? If no, then do you want to drop the objects without proper homes, or do you want to create a proper home for the objects
Performance can also matter, but really only if you expect either list to be very long or if you need to run this process multiple times (such as in a React component in the browser).
Implement
Loop through the list you chose. For each item, scan the other list for the relevant item(s): its home or its children. Take the appropriate action for those items depending on which plan you chose.
Another consideration is: don't mutate your arguments. That means you probably need to create copies of the two input arrays before you do the work. If the arrays contain objects rather than scalars, you can't just do array.slice() to create a copy.
For an array of POJOs, you can convert the source to a string and then back again to create a clone.
The array of dates will need special handling, because JSON.parse will not revive dates.
Mutating arguments is generally a bad practice, at least in the functional paradigm that underlies many popular front-end frameworks today. Plus, if you create your own copies of the input data, you can gain efficiency by moving items from the source arrays to the output array, which means that subsequent iterations won't have to re-examine items that have already been processed.

Reducing redundant information in a JSON object with javascript and combining values

I have a CSV of results that looks at a picture and makes a guess at whether or not a picture contains a certain attribute. In this case if the subject in the picture is male or female.
I'm converting this CSV to JSON with javascript/node and I want to take the attributes and their values and put them in an array inside of one object per pciture. Right now each line of the CSV measures and attribute but it means at least two lines per image.
Simple version of the csv:
path, detect_id, score, x-coord, y-coord, w-coord, h-coord, attribute, value
picture_1.jpg,0,1.44855535,74,54,181,181,genderf,0.024716798
picture_1.jpg,0,1.44855535,74,54,181,181,genderm,0.975283206
I can convert this CSV to JSON and then at least group items together by their path/filename.
But that leaves a lot of redundant information out there and I want to put my Attributes and their Value together in a nested object inside of the main one.
Like:
Path: picture_1.jpg
Attributes: [genderf: 0.025,
genderm: 0.985]
other_info: other info
Right now I'm using lodash to create the objects as you see below but if I try to map through the attributes I end up pushing out every element except the last one.
So I can create the object with the following code.
var result =
_([...arr1, ...arr2])
.concat()
.groupBy("path")
.value();
Where arr1 and arr2 is the data from one line of the output csv. All the information is the same except the attribute and its value.
That gets me this object:
{
"picture_1.jpg": [
{
"path": "picture_1.jpg",
"detect_id,": "0",
"score,": "1.44855535",
"coordinates": [
{
"x,": "74",
"y,": "54",
"w": "181",
"h": "181"
}
],
"attribute": "genderf",
"value": "0.024716798"
},
{
"path": "picture_1.jpg",
"detect_id,": "0",
"score,": "1.44855535",
"coordinates": [
{
"x,": "74",
"y,": "54",
"w": "181",
"h": "181"
}
],
"attribute": "genderm",
"value": "0.975283206"
}
]
}
Which at least groups pictures together based on the path heading but a lot of the information is redundant and this is just measuring one attribute.
You could just iterate all csv-lines and build an object/map while keeping track of already found file-names/paths. If you encounter a line whose path already exists in the map, just append the attribute/value pair. Something like this (note that I've changed the coords delimiter for the sake of simplicity and that it needs proper error handling):
const data = ["picture_1.jpg,0,1.44855535,74;54;181;181,genderf,0.024716798", "picture_1.jpg,0,1.44855535,74;54;181;181,genderm,0.975283206"];
function createImageDataMap(dataArr) {
const imageDataResult = {};
for (const imgData of dataArr) {
const currData = parseImgDataLine(imgData);
if (!imageDataResult[currData.path]) {
imageDataResult[currData.path] = {
attributes: [], other_info: {
score: currData.score,
detectId: currData.detectId,
coords: currData.coords
}
}
}
imageDataResult[currData.path].attributes.push({[currData.attribute]: currData.value});
}
return imageDataResult;
}
function parseImgDataLine(line) {
const attributes = line.split(',');
return {
path: attributes[0],
detectId: attributes[1],
score: attributes[2],
coords: attributes[3],
attribute: attributes[4],
value: attributes[5]
}
}
console.log(JSON.stringify(createImageDataMap(data)));
// prints {"picture_1.jpg":{"attributes":[{"genderf":"0.024716798"},{"genderm":"0.975283206"}],"other_info":{"score":"1.44855535","detectId":"0","coords":"74;54;181;181"}}}

how to create a javascript Set with objects?

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);

Javascript array of objects where an object also contains an array

I am new to Javascript and programming in general, I hope someone can help me.
I am trying to create a model where drugs distribute throughout the body in different compartments/organs. A compartment has an inflow of drug and an outflow of drug from another compartment.
I want to create a variable called body which contains an array of objects aka different compartments/organs which I can access the various parameters.
I can create the following
var mybody =
[
{ID:"1", fullname:"Arterial", inflowID:"0", outflowID:"2"},
{ID:"2", fullname:"Kidney", inflowID:"1", outflowID:"3"},
{ID:"3", fullname:"Vein", inflowID:"2", outflowID:"0"},
];
What I am trying to achieve is inflowID needs to be array of size 2, inflowID[0], inflowID[1], inflowID[2].
the following syntax dosnt work but you can see what I am trying to achieve
{ID:"1", fullname:"Arterial", inflowID[0]:"3", inflowID[1]:"4", inflowID[2]:"nil", outflowID:"2"},
I have tried using square, curly and curved brackets in various ways but cant work out what the correct syntax needs to be.
If I need to I can separate this out of my body array.
David
An array is build as simple as this:
[ element 1, element 2, element 3, ... , element n ]
Which you already got correct for your array of objects.
var mybody = [
{ID:"1", fullname:"Arterial", inflowID:["0", "0", "0"], outflowID:"2"},
{ID:"2", fullname:"Kidney", inflowID:["1", "1", "1"], outflowID:"3"},
{ID:"3", fullname:"Vein", inflowID:["2", "2", "2"], outflowID:"0"},
];
Here you are:
{ID:"1", fullname:"Arterial", inflowID:["3","4","nil"], outflowID:"2"}
try this out it might help you...
sample Example..
var mybody =
[
{
ID:"1",
fullname:"Arterial",
inflowID:["3","4","nil"],
outflowID:"2"
},
{
ID:"2",
fullname:"Kidney",
inflowID:["2","1","0"],
outflowID:"3"
},
{
ID:"3",
fullname:"Vein",
inflowID:["3","3","0"],
outflowID:"0"
},
];

What is the correct format of populating a JSON object with a nested array of objects?

I'm trying to create a JSON object with a nested array of JSON objects. What is the correct format of this?
Here is an example what I am trying to create:
{
"reviewCount": 96,
"reviews": [
{"name": "Sean Steinman", "date": "reviewed 2 weeks ago", "reviewContent": "Fantastic Service"},
{"name": "Ryan Lundell", "date": "reviewed in the last week", "reviewContent":"Ask for Scott!"}
]
}
Here is what I have so far:
var reviewObj = {
reviewCount: reviews.length,
reviews: [{name: , date: , reviewContent:}]
}
After I initialize it, I will fill it with a for loop that runs through an existing array of strings.
CLARIFICATION:
The array that I'm using to populate the JSON object is:
[
"\nSean Steinman\nreviewed 2 weeks ago\n Fantastic Service\n",
"\nRyan Lundell\nreviewed in the last week\n Ask for Scott!\n• • •\n"
]
So I'm creating a new array in my for with tmpArr = reviews[i].split('/n');, and then where I'm getting stuck is how to stick that into the JSON object as an object.
First, you're not building a "JSON" object. You're just building an object. It's not JSON until you JSON-encode it. {"name": "bob"} is not JSON, it's an object literal. '{"name": "bob"}', the string, is JSON.
Second, you cannot loop inside an object literal, which is what your second code example seems to indicate you're trying to do. Instead, you need to initialize you reviews property to an empty array, and then loop and append items to the array.
var reviews = [
"\nSean Steinman\nreviewed 2 weeks ago\n Fantastic Service\n",
"\nRyan Lundell\nreviewed in the last week\n Ask for Scott!\n• • •\n"
];
var reviewObj = {
reviewCount: reviews.length,
reviews: []
}
reviews.forEach(function(line) {
var review = line.split("\n");
reviewObj.reviews.push({name: review[0], date: review[1], reviewContent: review[2]});
});

Categories