I'm trying to take an array of many arrays that contain many objects and manipulate it into an array of objects.
So, let's say I have an array that looks like this:
[
[
{Name: 'Josh', email: 'josh#gmail.com', Points: 33},
{Name: 'Doug', email: 'doug#gmail.com', Points: 12}
],
[
{Name: 'Josh', email: 'josh#gmail.com', Points: 11},
{Name: 'Doug', email: 'doug#gmail.com', Points: 18}
],
[
{Name: 'Josh', email: 'josh#gmail.com', Points: 2},
{Name: 'Doug', email: 'doug#gmail.com', Points: 27}
]
]
The Desired outcome would be an array of objects that has a 'Points' property to hold an array of the points. So it would look like this:
[
{Name: 'Josh', email: 'josh#gmail.com', Points: [33, 11, 2]},
{Name: 'Doug', email: 'doug#gmail.com', Points: [12, 18, 27]}
]
This problem seems simple, but I can't seem to figure out the best way of doing this. If you want this data in a fiddle, I made this for you to play with: http://jsfiddle.net/Qhxzz/1/
Here's one way using .reduce() and .forEach(). (You'll need patches if you're supporting older browsers.)
var consolidated = data.reduce(function(holder, arr) {
arr.forEach(function(obj) {
if (obj.email in holder.emails)
holder.emails[obj.email].Points.push(obj.Points);
else {
holder.emails[obj.email] = obj;
holder.result.push(obj);
obj.Points = [obj.Points];
}
});
return holder;
}, {emails:{},result:[]}).result;
And actually, we could flatten out the original Array using concat.apply(), and be left with one monolithic Array to iterate.
flat = [].concat.apply([], data);
This turns your data into this structure:
[{Name:"Josh", email:"josh#gmail.com", Points:33},
{Name:"Doug", email:"doug#gmail.com", Points:12},
{Name:"Josh", email:"josh#gmail.com", Points:11},
{Name:"Doug", email:"doug#gmail.com", Points:18},
{Name:"Josh", email:"josh#gmail.com", Points:2},
{Name:"Doug", email:"doug#gmail.com", Points:27}]
And makes the consolidation a bit simpler by eliminating the need for the inner .forEach().
var consolidated = flat.reduce(function(holder, obj) {
if (obj.email in holder.emails)
holder.emails[obj.email].Points.push(obj.Points);
else {
holder.emails[obj.email] = obj;
holder.result.push(obj);
obj.Points = [obj.Points];
}
return holder;
}, {emails:{}, result:[]}).result;
Note that this depends on the input structure being static (no recursion here). Assuming you're not using any libraries:
var result = [];
for (var i = 0; i < initial.length; i++) {
var innerArray = initial[i];
for (var j = 0; j < innerArray.length; j++) {
var item = innerArray[j];
var found = false;
// search result for an object with a matching email prop
for (var k = 0; k < result.length; k++) {
if (result[k].email === item.email) {
found = true;
// add this Point to the Points array
result[k].Points.push(item.Points);
}
}
if (!found) {
// convert Points to an array of Points
item.Points = [item.Points];
result.push(item);
}
}
}
http://jsfiddle.net/Qhxzz/2/
Related
I have a declaration in a code i received to program a logic for. I have already figured out my algorithm, but i'm unable to figure out what datatype this is. I basically have to compare values of "skills" of every row to 'JavaScript' and if it is true i need to do a task. I'm unable to access the value of skills. What datatype is this declaration and how do I access it's values?
I have tried accessing the values using row/column of table type and also using arrays, but nothing works. For adding/removing rows to this table,
const newCandidates = [
{ name: "Kerrie", skills: ["JavaScript", "Docker", "Ruby"] },
{ name: "Mario", skills: ["Python", "AWS"] }
];
You have an array of dictionaries. You can access its items like this:
const newCandidates = [{
name: "Kerrie",
skills: ["JavaScript", "Docker", "Ruby"]
},
{
name: "Mario",
skills: ["Python", "AWS"]
}
];
console.log(newCandidates[0].skills[1])
console.log(newCandidates[1].name)
It is array in javascript. Though javascript arrays are nothing but objects.
const newCandidates = [
{ name: "Kerrie", skills: ["JavaScript", "Docker", "Ruby"] },
{ name: "Mario", skills: ["Python", "AWS"] }
];
console.log("DataType of newCandidates: ", typeof newCandidates); // prints object type
// accessing skills array in newCandidates
for(var i = 0; i < newCandidates.length; i++) {
let person = newCandidates[i];
console.log("personName: ", person["name"]);
// since skills is array, iterate through it.
for(var j = 0; j < person["skills"].length; j++) {
let currentSkill = person["skills"][j];
// do something with currentSkill
console.log("Skill-" + j + " : " + currentSkill);
}
}
You have an array of Javascript objects(everything in curly brackets). Do a forEach loop on the array:
newCandidates.forEach(e => console.log(e.skills))
This will give you the skills array. You can use additional array methods to test whether the skills contain "Javascript"
newCandidates.forEach(candidate => {
if(candidate.skills.includes("Javascript") {
*execute your function*
}
}
I have an array like this
students = [{name: 'Abbey', age: 25}, {name: 'Brian', age: 45},
{name: 'Colin', age: 25}, {name: 'Dan', age: 78}]
and I want the output to be;
uniqueAges = [45, 78]
To be clear, if there is an age value that appears more than once in the students array, I do not want any of the objects with that age in my uniqueAges array. 'Abbey' and 'Colin' have the same age so they are both out.
I know I can do something like this and run uniqueAgeGetter(students)
function uniqueAgeGetter(list){
var listCopy = list.slice();
var uniqueAges = list.slice();
for (var i = list.length - 1; i >= 0; i--) {
for (var j = listCopy.length - 1; j >= 0; j--) {
if(listCopy[j].name !== list[i].name &&
listCopy[j].age == list[i].age){
uniqueAges.splice(i, 1)
}
}
}
console.log(uniqueAges)
return uniqueAges
}
But is it possible to do it without a second loop? I'm not an expert on time complexity but I am trying to find if it is possible this task can be O(n).
Edit:
I am not asking if uniqueAgeGetter be rewritten to read nicer or use functions like map, reduce or filter (as my understanding is they are ultimately a loop as well).
My question is can uniqueAgeGetter be refactored in a way that reduces the time complexity? Can it be done with only one loop?
Thank you.
This can be done in O(n) time by counting the number of times an age has been seen, and filtering out the ages with a count more than one.
Since ages have reasonable limits, we can use an integer array of length equal to the maximum possible age to store the age counts. In the example below, I take the maximum possible age to be a comfortable 200.
var students = [
{name: 'Abbey', age: 25 },
{name: 'Brian', age: 45 },
{name: 'Colin', age: 25 },
{name: 'Dan', age: 78 }
];
var studentAges = students.map(val => val.age);
var ageCounts = Array(200).fill(0);
studentAges.forEach(age => ageCounts[age] += 1);
var uniqueAges = studentAges.filter(age => ageCounts[age] == 1);
console.log(uniqueAges);
The first idea, we can do over two step:
Step1: Sort the array
-- There are many algorithms to do it. As I know, currently, the complexity of best algorithm now is O(Nlog(N)) with N is the number of array.
Step2: Remove the duplicated elements
-- The complexity of this step is O(N)
So, over two steps, the complexity is O(N) + O(Nlog(N)). Finally, the complexity is O(Nlog(N))
The second idea
This also has the complexity is O(Nlog(N)) but it will be O(N) for next time you want to get the unique age.
Instead of saving the data in array, you can rebuild in a binary search tree with a little custom. This node in this tree will save all the elements with same age.
The complexity for the first time you build the tree is O(Nlog(N))
About the algorithm which has the complexity is O(N), currently, I think there are no technique to solve it. :D
You can use reduce
The first reduce is to summarise the array and convert it into an object using the age as a the key. Using the age as the key will make it easier to check if the age already exist. The object properties will have an array value like [2,true], where the first element is the age and the second element tells if the age has duplicates. Using Object.values will convert the object into an array.
The second reduce is to form the desired output.
let students = [{name: 'Abbey', age: 25 }, {name: 'Brian', age: 45 },{name: 'Colin', age: 25 }, {name: 'Dan', age: 78 }];
let uniqueAges = Object.values(students.reduce((c, v) => {
if (c[v.age] === undefined) c[v.age] = [v.age, true];
else c[v.age][1] = false;
return c;
}, {})).reduce((c, v) => {
if (v[1]) c.push(v[0]);
return c;
}, []);
console.log(uniqueAges);
Here is one way you could do it. I think the time complexity would be O(n^2) where n is the number of elements in the original array and m is the number of unique elements in the output array.
const students = [
{name: 'Abbey', age: 25 },
{name: 'Brian', age: 45 },
{name: 'Colin', age: 25 },
{name: 'Dan', age: 78 }
];
const uniqueStudents = students.map(val => val.age)
.sort()
.reduce((current, next) => {
return current.length === 0 ? [].concat(current, next)
: current[current.length - 1] !== next ? [].concat(current, next)
: current.slice(0, -1);
}, []);
console.log(uniqueStudents);
🚀 The fastest way with a single iteration.
const students = [
{name: `Abbey`, age: 25},
{name: `Brian`, age: 45},
{name: `Colin`, age: 25},
{name: `Dan`, age: 78},
{name: `Dan`, age: 25}
]
// no global variables
function unique(key) {
const occurrences = {}
const list = {}
return (result, next, index, {length}) => {
const value = next[key]
if (list[value]) {
occurrences[value] = value
}
else {
list[value] = value
result.push(value)
}
return index === length - 1 ? result.filter(v => !occurrences[v]) : result
}
}
const uniqueNames = students.reduce(unique(`name`), [])
const uniqueAges = students.reduce(unique(`age`), [])
console.log(uniqueAges)
for getting unique elements
const data = [128, 128,128,121,127,127,121,121,121,121,122,125];
const uniqueData = Object.keys(data.reduce((r,c) => {r[c] = true; return r;}, {}))
console.log(uniqueData)
But doing this will sort the array and will not keep the original order of the array
Complexity O(n)
I have a JS array with the following structure:
[
{group: 'group1', addons: [{name: 'addon1', price: '100'}, {name: 'addon2', price: '130'}] },
{group: 'group2', addons: [{name: 'addon3', price: '150'}] },
{group: 'group3', addons: [{name: 'addon4', price: '200'}] }
]
So it's an array of groups, each containing an array of addons.
How do I implement a getAddon(addonName) method with the help of lodash? It's very simple to do with 2 for/foreach loops, but maybe there is a simpler way?
For example getAddon('addon1') should return {name: 'addon1', price: '100'} - first found add-on entry.
How I implemented it, sorry for typescript code:
public getAddon(addonName: string): _IAddonModel {
if (!this.groups) return null;
//todo refactor using underscore/lodash
for (var i = 0; i < this.groups.length; i++) {
var group = this.groups[i];
for (var j = 0; j < group.addons.length; j++) {
var addon = group.addons[j];
if (addon && addon.name === addonName) {
return addon;
}
}
}
return null;
}
Thanks.
var result = _(data)
.pluck('addons')
.flatten()
.findWhere({name: 'addon3'});
http://jsfiddle.net/4g4wbu13/1/
So you - pluck() all the nested addons arrays, flatten() them in a single level depth array, and use findWhere() to get the specific item.
I have this Array and Object representing the same data:
arrayExample = [
{name: "max", age: 21},
{name: "max.David", age: 27},
{name: "max.Sylvia"},
{name: "max.David.Jeff"},
{name: "max.Sylvia.Anna", age: 20},
{name: "max.David.Buffy"},
{name: "max.Sylvia.Craig"},
{name: "max.Sylvia.Robin"}
];
ObjectExample = {
name: "max",
age: 21,
children: [
{
name: "Sylvia",
children: [
{name: "Craig"},
{name: "Robin"},
{name: "Anna", age: 20}
]
},
{
name: "David",
age: 27,
children: [
{name: "Jeff"},
{name: "Buffy"}
]
}
]
};
my objective is to extend the Array class to have 2 functions flatten which transform the objectExample into the arrayExample and uneven which do the opposite, I'm thinking maybe lodash would help here but I still didn't find the correct way to do this here's where I'm now:
to flatten from objectExample to arrayExample first the objectExample structure must be specific meaning the parents must share a property with all their children sure the parents and children could have other property that should be ported to the proper item in the new arrayExample, also for the uneven function it should create an object that all the parents share the same property with their children and other property should be copied respectively.
To give my use case for this I'm trying to make a d3js tree layout of angular ui router in my application that will be generated from the routes JSON file since I make the routes in a JSON file.
update:
my specific problem is that I need to create a d3js tree layout for angular-ui-router configurations states object which I can extract into a json file as I said before, the structure for the ui-router is like the arrayExample, and the required structure for the d3js tree layout is like the objectExample, one way to go about this is to manually rewrite it and it wont take too much time but that solution is not what I want I need to make a build task for this for generic routes that will always have the name attribute in their config object that could be used to find children of each route or state, for more information check ui-router for routes config object and this d3 videos for d3 tre layout:
part 1.
part 2.
correction: extending the Object class with a flatten function to flatten an object into an array and the Array class with unEven function to unEven an array into an object not like I wrote before:
my objective is to extend the Array class to have 2 functions.
update 2:
To make this more clear, both flatten and unEven are like the map function except flatten is for an object not an array and it return an array, and the unEven function is for an array but return an object.
Here's a function that will produce the flattened output:
Working demo: http://jsfiddle.net/jfriend00/w134L7c6/
var ObjectExample = {
name: "max",
age: 35,
status: "single",
hometown: "Scottsdale",
children: [
{
name: "Sylvia",
children: [
{name: "Craig", age: 16},
{name: "Robin"},
{name: "Anna"}
]
},
{
name: "David",
age: 54,
children: [
{name: "Jeff"},
{name: "Buffy"}
]
}
]
};
// call this on an object with a name property
// and an optional children property (which would be an array of objects)
function flatten(obj, key, outputArray, rootName) {
var name, item;
outputArray = outputArray || [];
rootName = rootName || "";
if (rootName) {
rootName += ".";
}
if (obj.hasOwnProperty(key)) {
name = rootName + obj[key];
item = {};
item[key] = name;
for (var prop in obj) {
if (obj.hasOwnProperty(prop) && prop !== "children") {
item[prop] = obj[prop];
}
}
outputArray.push(item)
if (obj.children) {
for (var i = 0; i < obj.children.length; i++) {
flatten(obj.children[i], key, outputArray, name);
}
}
}
return outputArray;
}
var result = flatten(ObjectExample, "name");
Produces this output:
[{"name":"max","age":35,"status":"single","hometown":"Scottsdale"},
{"name":"max.Sylvia"},
{"name":"max.Sylvia.Craig","age":16},
{"name":"max.Sylvia.Robin"},
{"name":"max.Sylvia.Anna"},
{"name":"max.David","age":54},
{"name":"max.David.Jeff"},
{"name":"max.David.Buffy"}]
You could adapt this function to be a method on the Array prototype if you really want to (not something I would recommend, particularly since the input isn't even an array).
I do not know what you mean when you say "the rootName could have more then one". ObjectExample is an object and thus cannot have more than one name at the top level. If you started with an array of ObjectExample like structures, then you could just loop over the array calling flatten() on each object in the top level array and it would accumulate the results.
If I have an array of strings, I can use the .join() method to get a single string, with each element separated by commas, like so:
["Joe", "Kevin", "Peter"].join(", ") // => "Joe, Kevin, Peter"
I have an array of objects, and I’d like to perform a similar operation on a value held within it; so from
[
{name: "Joe", age: 22},
{name: "Kevin", age: 24},
{name: "Peter", age: 21}
]
perform the join method only on the name attribute, to achieve the same output as before.
Currently I have the following function:
function joinObj(a, attr){
var out = [];
for (var i = 0; i < a.length; i++){
out.push(a[i][attr]);
}
return out.join(", ");
}
There’s nothing wrong with that code, it works, but all of a sudden I’ve gone from a simple, succinct line of code to a very imperative function. Is there a more succinct, ideally more functional way of writing this?
If you want to map objects to something (in this case a property). I think Array.prototype.map is what you're looking for if you want to code functionally.
(fiddle)
If you want to support older browsers, that are not ES5 compliant you can shim it (there is a polyfill on the MDN page above). Another alternative would be to use underscorejs's pluck method:
var users = [
{name: "Joe", age: 22},
{name: "Kevin", age: 24},
{name: "Peter", age: 21}
];
var result = _.pluck(users,'name').join(",")
Well you can always override the toString method of your objects:
var arr = [
{name: "Joe", age: 22, toString: function(){return this.name;}},
{name: "Kevin", age: 24, toString: function(){return this.name;}},
{name: "Peter", age: 21, toString: function(){return this.name;}}
];
var result = arr.join(", ");
console.log(result);
On node or ES6+:
users.map(u => u.name).join(', ')
I've also come across using the reduce method, this is what it looks like:
console.log(
[
{name: "Joe", age: 22},
{name: "Kevin", age: 24},
{name: "Peter", age: 21}
]
.reduce(function (a, b) {
return (a.name || a) + ", " + b.name}
)
)
The (a.name || a) is so the first element is treated correctly, but the rest (where a is a string, and so a.name is undefined) isn't treated as an object.
Edit: I've now refactored it further to this:
x.reduce(function(a, b) {return a + ["", ", "][+!!a.length] + b.name;}, "");
which I believe is cleaner as a is always a string, b is always an object (due to the use of the optional initialValue parameter in reduce)
Edit 6 months later: Oh what was I thinking. "cleaner". I've angered the code Gods.
I don't know if there's an easier way to do it without using an external library, but I personally love underscore.js which has tons of utilities for dealing with arrays, collections etc.
With underscore you could do this easily with one line of code:
_.pluck(arr, 'name').join(', ')
lets say the objects array is referenced by the variable users
If ES6 can be used then the easiest solution will be:
users.map(user => user.name).join(', ');
If not, and lodash can be used so :
_.map(users, function(user) {
return user.name;
}).join(', ');
const lists = [
{name: "Joe", age: 22},
{name: "Kevin", age: 24},
{name: "Peter", age: 21}
]
const joinedStr = lists.map((list) => list.name).join(" ")
console.log('joined',joinedStr)
This should do it since map will return an array of strings and you can join it then
If object and dynamical keys: "applications\":{\"1\":\"Element1\",\"2\":\"Element2\"}
Object.keys(myObject).map(function (key, index) {
return myObject[key]
}).join(', ')
An old thread I know but still super relevant to anyone coming across this.
Array.map has been suggested here which is an awesome method that I use all the time.
Array.reduce was also mentioned...
I would personally use an Array.reduce for this use case.
Why? Despite the code being slightly less clean/clear. It is a much more efficient than piping the map function to a join.
The reason for this is because Array.map has to loop over each element to return a new array with all of the names of the object in the array. Array.join then loops over the contents of array to perform the join.
You can improve the readability of jackweirdys reduce answer by using template literals to get the code on to a single line. "Supported in all modern browsers too"
// a one line answer to this question using modern JavaScript
x.reduce((a, b) => `${a.name || a}, ${b.name}`);
not sure, but all this answers tho they work but are not optiomal since the are performing two scans and you can perform this in a single scan. Even though O(2n) is considered O(n) is always better to have a true O(n).
const Join = (arr, separator, prop) => {
let combined = '';
for (var i = 0; i < arr.length; i++) {
combined = `${combined}${arr[i][prop]}`;
if (i + 1 < arr.length)
combined = `${combined}${separator} `;
}
return combined;
}
This might look like old school, but allows me to do thig like this:
skuCombined = Join(option.SKUs, ',', 'SkuNum');
you can convert to array so get object name
var objs = [
{name: "Joe", age: 22},
{name: "Kevin", age: 24},
{name: "Peter", age: 21}
];
document.body.innerHTML = Object.values(objs).map(function(obj){
return obj.name;
});
This worked for me:
var users = [
{name: "Joe", age: 22},
{name: "Kevin", age: 24},
{name: "Peter", age: 21}
]
return users.map((user: { name: string; }) => user.name).join(", ");
try this
var x= [
{name: "Joe", age: 22},
{name: "Kevin", age: 24},
{name: "Peter", age: 21}
]
function joinObj(a, attr) {
var out = [];
for (var i=0; i<a.length; i++) {
out.push(a[i][attr]);
}
return out.join(", ");
}
var z = joinObj(x,'name');
z > "Joe, Kevin, Peter"
var y = joinObj(x,'age');
y > "22, 24, 21"
Easiest way:
const oldArrayOfObjects = [
{name: "Bob", age:40},
{name: "Andrew", age:25},
{name: "Peter", age:30}];
const newArrayOfStringNames = oldArrayOfObjects.map((OA) => OA.name);
const newArrayOfAges = oldArrayOfObjects.map((OA) => OA.age);
console.log({newArrayOfStringNames, newArrayOfAges})