Related
I have read several solutions to this problem here. When I try it, I continue to receive an error for the pop() method.
I have what is essentially a multidimensional array in javascript.
I am tasked with returning the array with the sensitive info removed (e.g. remove the SSN, in this example)
I thought I could use a foreach loop, and the pop() function to remove the last element of the child arrays, the SSN.
testing it using node on the commandline, the stdout is telling me that element.pop() is not a function. i've tried it with pop(), slice(), filter(), all with no success.
when running $> node filename.js
H:\Apache2\htdocs\test\filename.js:50
noppi[i] = element.pop();
^
TypeError: element.pop is not a function
let recs = [
{
ID: 1,
NAME: 'John',
EMAIL: 'john#example.com',
SSN: '123'
}, {
ID: 2,
NAME: 'Sally',
EMAIL: 'sally#example.com',
SSN: '456'
}, {
ID: 3,
NAME: 'Angie',
EMAIL: 'angie#example.com',
SSN: '789'
}
];
let i = 0;
let noppi = [];
recs.forEach(element => {
noppi[i] = element.pop();
i++;
});
console.log(noppi);
At the risk of sounding redundant, I'll briefly reiterate what the earlier answers have already stated.
The input data structure isn't a multi-dimensional array [ [ ... ], [ ... ] ] , it's an array of objects [ {...}, {...} ]. So you can't use Array methods like .pop() on the objects {...}.
Here's a simple one-liner that uses .forEach() and delete.
recs.forEach(obj => delete obj.SSN)
delete is an operator with one purpose: to remove an object's property like for example SSN: '123-45-6789'. Simple and perfect.
Note, .forEach() mutates the array, meaning that it's the original data being changed (see Minja's comment).
let recs = [
{
ID: 1,
NAME: 'John',
EMAIL: 'john#example.com',
SSN: '123'
}, {
ID: 2,
NAME: 'Sally',
EMAIL: 'sally#example.com',
SSN: '456'
}, {
ID: 3,
NAME: 'Angie',
EMAIL: 'angie#example.com',
SSN: '789'
}
];
recs.forEach(obj => delete obj.SSN);
console.log(recs)
Try this:
recs.forEach(element => {
noppi.push = element;
});
You are trying to use pop() on an object not an array
As per your need you need to remove SSN from your object, try below code it should work for you.
recs.forEach(element => {
const { SSN, ...rest } = element;
noppi.push(rest);
});
Here we are removing SSN from object and rest will push in noppi.
I know indexOf cannot be used in an array of objects, e.g. below.
const objs = [
{name: 'darling', age: 28},
{name: 'eliot', age: 29}
]
console.log(objs.indexOf({ name: 'eliot', age: 29 })) // print -1
However, why DOM element array index can be traced by indexOf??
e.g. below
document.getElementById('slides').addEventListener('click', function (e) {
const nodes = document.querySelectorAll('#slides > .slide')
const newNodes = [...nodes]
console.log(newNodes);
console.log(newNodes.indexOf(e.target)) // can print out the index
})
Every time the interpreter comes across an object literal when it runs a line - for example:
const objs = [
{name: 'darling', age: 28},
{name: 'eliot', age: 29} // this line
]
or
console.log(objs.indexOf(
{ name: 'eliot', age: 29 } // this line
))
It creates a new object with those properties. They aren't the same.
console.log(
// two separate objects, which happen to contain the same value
{ foo: 'foo' } === { foo: 'foo' }
);
In contrast, when looking up nodes in the DOM, the nodes are generally static - they don't change unless JavaScript runs which explicitly does so. The e.target refers to one such static node, and querySelectorAll returns a collection of such static nodes.
So they're ===, and .indexOf works.
Consider the following behavior:
const element = { foo: 1 };
const arr = [{ foo: 1 }, { foo: 2 }, element];
console.log(arr.indexOf({ foo: 1 })); // -1
console.log(arr.indexOf(element)); // 2
console.log(arr.indexOf(arr[0])); // 0
It clearly demonstrates how indexOf matches the reference to an object, rather than an object with the same keys and values.
For one { name: 'eliot', age: 29 } is a new object and hence not found in your array
So you can find an object if it is the actual same object
Otherwise look for it by the value of one or more of the entries
const objs = [
{name: 'darling', age: 28},
{name: 'eliot', age: 29}
]
const objToFind = objs[1]
console.log(objs.indexOf(objToFind))
// find the index by name
const idxOfEliot = objs.findIndex(({name}) => name==="eliot")
console.log(idxOfEliot)
I found unexpected result when try to merge with lodash object with flat array inside.
Here the example:
var people = { name: 'Andrew', age: '30', values: ["PT", "PORTA 31"] };
const person = { age: '31', values: ["PT"] };
var people2 = { name: 'Andrew', age: '30', values: [{ pippo : 1}] };
const person2 = { age: '31', values: [{ pippo : 2}] };
// Now merge person back into people array
console.log(_.merge({}, people, person));
console.log(_.merge({}, people2, person2));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.min.js"></script>
The result of first console.log is
{
age: "31",
name: "Andrew",
values: ["PT", "PORTA 31"]
}
And not as expected
{
age: "31",
name: "Andrew",
values: ["PT"]
}
Someone can explain me why and give me a solution to make sure that with a flat array it takes me the correct value
I think assign is better in this case than merge
This method is like _.assign except that it recursively merges own and inherited enumerable string keyed properties of source objects into the destination object. Source properties that resolve to undefined are skipped if a destination value exists. Array and plain object properties are merged recursively. Other objects and value types are overridden by assignment. Source objects are applied from left to right. Subsequent sources overwrite property assignments of previous sources.
var people = { name: 'Andrew', age: '30', values: ["PT", "PORTA 31"] };
const person = { age: '31', values: ["PT"] };
console.log(_.assign({}, people, person));
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.20/lodash.min.js"></script>
I believe _.assign(people, person) would produce the desired outcome in this case https://lodash.com/docs/4.17.15#assign
This functionality is also native and can be used like this Object.assign(target, source)
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})