Related
I have an array of objects something like this
arr = [{"class":"section"},{"fieldGroup":[]},{"class":"col","name":"input"},{"class":"col","name":"dropdown"},{"class":"col","name":"date"},{"fieldGroup":[]},{"class":"col","name":"table"}]
Now I need to move the objects with "class":"col" inside its previous object which contains "fieldGroup"
So my desired output should be like this
arr = [{"class":"section"},{"fieldGroup":[{"class":"col","name":"input"},{"class":"col","name":"dropdown"},{"class":"col","name":"date"}]},{"fieldGroup":[{"class":"col","name":"table"}]}]
I have tried this piece of code
arr.forEach((item: any, index: number) => {
if (item.class === "col") {
arr[index - 1].fieldGroup.push(item);
arr.splice(index, 1);
}else {
return arr;
}
})
but getting an error saying cannot read property push of undefined
any help?
Your problem is that when you remove an item from your array using .splice() all the items in your arr shift down one index. For example, the item that used to be at index 3 will now be at index 2, the item that used to be at index 4 is will now be at index 3 etc. Your .forEach() loop doesn't take this shift into account, and so once you remove the item at index 2 with .splice() your index changes to 3 for the next iteration, but really, you need to look at index 2 again as your array items have shifted down an index. You can fix this by using a standard for loop and decrementing the index counter when you remove an item to look at th inded again:
const arr = [{"class":"section"},{"fieldGroup":[]},{"class":"col","name":"input"},{"class":"col","name":"dropdown"},{"class":"col","name":"date"},{"fieldGroup":[]},{"class":"col","name":"table"}];
for(let i = 0; i < arr.length; i++) {
const item = arr[i];
if (item.class === "col") {
arr[i - 1].fieldGroup.push(item);
arr.splice(i, 1);
i--;
}
}
console.log(arr);
try it ...
var arr = [{"class":"section","name":"input"},{"fieldGroupName":"one","fieldGroup":[]},{"class":"col","name":"input"},{"class":"col","name":"dropdown"},{"class":"section","name":"input"},{"fieldGroupName":"one","fieldGroup":[]},{"class":"col","name":"date"}]
arr.forEach((elm , index) =>{
if(elm.class == 'col'){
arr[1].fieldGroup.push(elm)
arr.splice(index, 1)
}
})
console.log(arr);
Try this:
var arr = [{
"class": "section"
},
{
"fieldGroup": []
},
{
"class": "col",
"name": "input"
},
{
"class": "dropdown"
},
{
"class": "date"
},
{
"fieldGroup": []
},
{
"class": "col",
"name": "table"
}
];
let fieldGroupPosition;
let finalArr = [];
let lastGroupPosition = null;
arr.forEach((item, index) => {
if (Array.isArray(item.fieldGroup)) {
finalArr.push({
fieldGroup: []
});
lastGroupPosition = finalArr.length - 1;
} else {
if (lastGroupPosition)
finalArr[lastGroupPosition].fieldGroup.push(item);
else
finalArr.push(item);
}
});
console.log(finalArr);
var a = [
{ class: "section" },
{ fieldGroup: [] },
{ class: "col", name: "input" },
{ class: "dropdown" },
{ class: "date" },
{ fieldGroup: [] },
{ class: "col", name: "table" },
];
var res = [];
var currFieldGroup = null;
for (var i in a) {
if ("fieldGroup" in a[i]) {
currFieldGroup = a[i];
res.push(a[i]);
} else if ("class" in a[i]) {
if (currFieldGroup) {
currFieldGroup.fieldGroup.push(a[i]);
} else {
res.push(a[i]);
}
}
}
console.log(res);
I created a function to check if there are repeated cell phone numbers in a list. The problem is that I did this by using nested for. How could I optimize this code with functional programming ?
checkDuplicate(): boolean {
for (let i = 0; i < this.phoneList.length; i++) {
for (let j = 0; j < this.phoneList.length; j++) {
if (i != j) {
if (this.phoneList[i].number === this.phoneList[j].number) {
this.toastrService.error('Phone already in List!');
return true;
}
}
}
}
return false;
}
You can make a Set containing only the unique numbers and compare the length of the Set to the length of the original array
hasDuplicates(): boolean {
return new Set(this.phoneList.map(p => p.number)).size < this.phoneList.length
}
O(n) solution
It's not a functional but it's the fastest so far.
const checkDuplicate = (phones)=> {
let counts = {};
for(let phone of phones) {
if(counts[phone.number]) return true;
counts[phone.number] = 1;
}
return false;
}
if(checkDuplicate(this.phoneList)) {
this.toastrService.error('Phone already in List!');
}
even better than filter (which i suggested in a comment) use Set - there are a few ways to do it but this is pretty clean. However .filter() would probably be considered more 'functional' as it is a HOC
let a = [1,2,1,3,3,5]
let x = [...new Set(a)]
// => [1, 2, 3, 5]
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
You could do something like:
let dups = this.phoneList.filter(item =>
this.phoneList.filter(item2 => item.number == item2.number).length > 1
);
if (dups.length) {
this.toastrService.error('Phone already in List!');
return true;
}
...though it suffers a little for readability.
It's not really about angular but just the JavaScript. You could just short cycle the loop on the list as faster.
Each inner loop is n-i faster (less to do/check) since we already checked those
var xObj = {
phoneList: [{
name: "freddy",
number: 55512121234
}, {
name: "Jimmy",
number: 55512121234
}, {
name: "Mommy",
number: 55512121233
},
{
name: "Tommy",
number: 55512121244
},
{
name: "Luka",
number: 55512121222
},
{
name: "Penny",
number: 55512121255
},
{
name: "Sammy",
number: 55512121266
},
{
name: "Bill",
number: 55512121244
}
],
phoneList2: [{
name: "freddy",
number: 55512121234
}, {
name: "Jimmy",
number: 55512121235
}, {
name: "Mommy",
number: 55512121233
},
{
name: "Tommy",
number: 55512121244
},
{
name: "Luka",
number: 55512121222
},
{
name: "Penny",
number: 55512121259
},
{
name: "Sammy",
number: 55512121266
},
{
name: "Bill",
number: 55512121247
}
],
toastrService: {
error: function(message) {
console.log(message);
}
},
checkDuplicate: function() {
let hasDupe = false
for (let i = 0; i < this.phoneList.length; i++) {
for (let j = i + 1; j < this.phoneList.length; j++) {
if (this.phoneList[i].number === this.phoneList[j].number) {
hasDupe = true;
break;
}
}
if (hasDupe) break;
}
if (hasDupe) this.toastrService.error('Phone already in List!');
return hasDupe;
},
checkDuplicate2: function() {
let hasDupe = false
for (let i = 0; i < this.phoneList2.length; i++) {
for (let j = i + 1; j < this.phoneList2.length; j++) {
if (this.phoneList2[i].number === this.phoneList2[j].number) {
hasDupe = true;
break;
}
}
if (hasDupe) break;
}
if (hasDupe) this.toastrService.error('Phone already in List!');
return hasDupe;
}
};
let cdup = xObj.checkDuplicate();
let cdup2 = xObj.checkDuplicate2();
console.log("dup:", cdup, cdup2);
You can use Array.some to check if a phone number is a duplicate, as shown below. In the loop callback, the phone number is the key of a boolean value added to the exists object. The loop stops as soon as the callback function returns true, which happens when a key/value corresponding to the loop item is found in exists.
checkDuplicate(): boolean {
let exists: { [key: number]: boolean } = {};
return this.phoneList.some(phoneListItem => {
if (exists[phoneListItem.number]) {
return true;
} else {
exists[phoneListItem.number] = true;
return false;
}
});
}
See this stackblitz for a demo.
I have model like this:
var model = [{id: 1, prices: [{count: 2}, {count: 3}]}, {id: 2, prices: [{count: 2}]}, {id: 3, prices: [{count: 3}]}];
and I need to filter this objects of array useing property count and I will need to return matched objects in three scenarios:
if the objects have two objects in array prices,
if the objects have one object in array prices matching count:2,
if the objects have one property in array prices matching count:3.
so..when i click the button without assigned value i wanna see all objects, when i click button with value = 2 i wanna see objects with count: 2 and when i click the button with value = 3 i wanna get objects with count: 3, i must do this in AngularJS –
maybe something like this?
var result = model.filter(function(m) {
// make sure the m.prices field exists and is an array
if (!m.prices || !Array.isArray(m.prices)) {
return false;
}
var numOfPrices = m.prices.length
if (numOfPrices === 2) { // return true if its length is 2
return true;
}
for (var i = 0; i < numOfPrices; i++) {
if (m.prices[i].count &&
(m.prices[i].count === 2 ||
m.prices[i].count == 3)) {
return true;
}
}
return false;
});
use lodash or underscore library.. and then your code with lodash will be like:
_.filter(model, function(i){
return _.intersection(_.map(i.prices, 'count'), [3,2]).length;
})
it returns items that on their price property have array which contains element with count = 3 or count = 2
var model = [{
id: 1,
prices: [{
count: 2
}, {
count: 3
}]
}, {
id: 2,
prices: [{
count: 2
}]
}, {
id: 3,
prices: [{
count: 3
}]
}];
var search = function(data) {
var result = {};
function arrayObjectIndexOf(myArray, searchTerm, property) {
for (var i = 0, len = myArray.length; i < len; i++) {
if (myArray[i][property] === searchTerm) return i;
}
return -1;
}
for (var index in data) {
if (data[index].hasOwnProperty("prices") && arrayObjectIndexOf(data[index].prices, 2, 'count') != -1) {
result[data[index].id] = data[index];
} else if (data[index].hasOwnProperty("prices") && arrayObjectIndexOf(data[index].prices, 3, 'count') != -1) {
result[data[index].id] = data[index];
} else if (data[index].hasOwnProperty("prices") &&
data[index].prices.length == 2) {
result[data[index].id] = data[index];
}
}
return result;
}
var output = search(model);
console.log(output);
I have a multidimensional array but the ID's are unique across parents and children, so I have a problem looping through using a for loop. The problem is that I cannot seem to grab the ID of the children. How do you think I should handle this?
var Options = [
{
id: 0,
children: []
},
{
id: 2,
children: []
},
{
id: 3,
children: [
{
id: 4,
children: []
},
{
id: 5,
children: []
},
{
id: 6,
children: []
}
]
},
{
id: 7,
children: [
{
id: 8,
children: []
},
{
id: 9,
children: []
}
]
}
];
I have kept the code concise for the sake of brevity. What I am trying to do is iterate through the array to compare ID's.
This does not look like a "multidimensional array", but rather like a tree. Looping one level can be done with a simple for-loop:
for (var i=0; i<Options.length; i++) // do something
To loop the tree in-order, you will need a recursive function:
function loop (children, callback) {
for (var i=0; i<children.length; i++) {
callback(children[i]);
loop(children[i].children, callback);
}
}
loop(Options, console.log);
To get all children by their id, so that you can loop through the ids (regardless of the tree structure), use a lookup table:
var nodesById = {};
loop(Options, function(node) {
nodesById[node.id] = node;
});
// access:
nodesById[4];
…and to loop them sorted by id, you now can do
Object.keys(nodesById).sort(function(a,b){return a-b;}).forEach(function(id) {
var node = nodesById[id];
// do something
});
How about recursion?
var findById = function (arr, id) {
var i, l, c;
for (i = 0, l = arr.length; i < l; i++) {
if (arr[i].id === id) {
return arr[i];
}
else {
c = findById(arr[i].children, id);
if (c !== null) {
return c;
}
}
}
return null;
}
findById(Options, 8);
Ah, use recursion :D
var Options = "defined above";//[]
var OptionArray = []; //just as an example (not sure what you want to do after looping)
(function looper(start){
for( var i = 0, len = start.length; i < len; i++ ){
var currentOption = start[i];
if( currentOption.id > 3 ){//could be more complex
OptionArray.push(currentOption);
}
if( currentOption.children.length > 0 ){
looper(currentOption.children);
}
}
})(Options);
So I have an object, that I'm using in nodejs. It looks as such:
for(var i = 0; i < x.length; i++) {
var sUser = x[i];
mUsers[sUser.userid] = CreateUser(sUser);
++mUsers.length;
}
So I'm pulling information from an external source, and it breaks down as an array full of instances of this:
[{ name: 'Michael Lovesllamas Lankford',
created: 1338420951.11,
laptop: 'pc',
laptop_version: null,
userid: '4fc6aed7eb35c14ad6000057',
acl: 0,
fans: 1,
points: 5,
avatarid: 34 }]
and so forth.
so that information is passed as x in the above function.
global.mUsers = {length:0}
global.UserBase = {
userid: -1,
name: "noidea",
isSuperUser: false,
isDJ: false,
laptop: "pc" };
process.on("registered", OnRegistered);
global.OnRegistered = function(a) {
//misc code here
RegisterUsers(a.users);
//misc code here
}
global.CreateUser = function(a) {
var b = UserBase;
b.userid = a.userid;
b.name = a.name;
b.laptop = a.laptop;
if (a.acl > 0) b.isSuperUser = true;
return b;
};
global.RegisterUsers = function(x) {
for(var i = 0; i < x.length; i++) {
var sUser = x[i];
mUsers[sUser.userid] = sUser;
++mUsers.length;
}
}
Now, I've logged it in the loop, and mUsers[sUser.userid] does indeed = sUser.
but when I console.log(mUsers) immediately after the loop, I get this:
{
userid1: { userid: userid3, name: name3, item: item3 },
userid2: { userid: userid3, name: name3, item: item3 },
userid3: { userid: userid3, name: name3, item: item3 }
}
And I don't know why it's overwriting. Any ideas?
The main problem is that you where continuously referencing the same object when you where calling CreateUser, as such it was simply updating and returning a reference which was being kept through out all the calls, this is why when you where printing it, it just printed the last update.
You need to create a copy of the object.
global.CreateUser = function(a) {
var b = Object.create(UserBase); // this will create a copy of it.
b.userid = a.userid;
b.name = a.name;
b.laptop = a.laptop;
if (a.acl > 0) b.isSuperUser = true;
return b;
};
now CreateUser is actually creating a copy, when you go through the properties the default ones may not appear right away, but theres still there, they've being simply moved to __proto__ you can still call them.
Try the below it is working for me
var obj = {
userid1: { userid: "userid1", name: "name3", item: "item3" },
userid2: { userid: "userid2", name: "name3", item: "item3" },
userid3: { userid: "userid3", name: "name3", item: "item3" }
};
var muser = {};
for (var key in obj) {
muser[key] = obj[key];
}