I need to achieve the same output but as you see the length of the ID's array is zero because I cannot achieve this output using push command, it generates errors like:
push is not a function
Cannot use indexOf for undefined or false
I need to solve this array with a push command and make the output exactly like below but I cannot use the each function because the length is zero.
var RepeaterClass = {
Repeaters: [],
collectRepeaterValues: function (rep_id, cat_id, element_id) {
this.Repeaters[rep_id] = this.Repeaters[rep_id] || [];
this.Repeaters[rep_id][cat_id] = this.Repeaters[rep_id][cat_id] || [];
if (-1 === this.Repeaters[rep_id][cat_id].indexOf(element_id)) {
this.Repeaters[rep_id][cat_id].push(element_id);
}
},
};
Implementing of this code:
ID_1: Array(0)
category: Array(1)
0: "dog"
animals: Array(2)
0: "dog"
1: "cat"
As others have commented it's not entirely clear what you're asking here. This code sort of works if you fix the line var Repeaters =[];
I think the confusion is arising because we create Repeaters as an array, but then I think you must be calling collectRepeaterValues with strings for rep_id and cat_id (e.g. 'ID_1' and 'animals') to get the output you are showing. It should be called with numbers if you want to create arrays. You can't access an array element with a string.
If you call with strings JavaScript is going to create object properties on the array when you do Repeaters[rep_id] = Repeaters[rep_id] || []. That is to say, if we execute the statement Repeaters['ID_1'] = [] in JavaScript it's not doing array assignment even if Repeaters is an array. It will create an object property called ID_1 and makes its value the empty array.
The snippets below show calling the (corrected) object with numbers and with strings and the results.
As an aside, the if statement in collectRepeaterValues is not working.
Now we're back on what the question really is. Do you want arrays, which have to be indexed by numbers of course, or do you want objects with string properties?
// CALLING WITH STRINGS
var RepeaterClass = {
Repeaters: [], // Fixed so it's an object property
collectRepeaterValues: function (rep_id, cat_id, element_id) {
this.Repeaters[rep_id] = this.Repeaters[rep_id] || [];
this.Repeaters[rep_id][cat_id] = this.Repeaters[rep_id][cat_id] || [];
if (-1 === this.Repeaters[rep_id][cat_id].indexOf(element_id)) {
this.Repeaters[rep_id][cat_id].push(element_id);
}
}
}
// What I think you're doing?
RepeaterClass.collectRepeaterValues("ID_1", "category", "dog");
RepeaterClass.collectRepeaterValues("ID_1", "animals", "dog");
RepeaterClass.collectRepeaterValues("ID_1", "animals", "cat");
// At the top level RepeaterClass.Repeaters is an empty array with a 'ID_1' property
// Array length is zero...
console.log(RepeaterClass.Repeaters.length); // 0
// But we have a property ID_1, that is itself an array of zero length with category
// and animals properties that are arrays
console.log(RepeaterClass.Repeaters.ID_1.category[0]); // dog
console.log(RepeaterClass.Repeaters.ID_1.animals[0]); // dog
console.log(RepeaterClass.Repeaters.ID_1.animals[1]); // cat
// Note that this IS the result at the end of the question
// EDIT: You can iterate over the properties with for..in
console.log('Iterating categories on ID_1:');
for (var cat_id in RepeaterClass.Repeaters.ID_1) {
console.log(cat_id);
}
// CALLING WITH NUMBERS
var RepeaterClass = {
Repeaters: [], // Fixed so it's an object property
collectRepeaterValues: function (rep_id, cat_id, element_id) {
this.Repeaters[rep_id] = this.Repeaters[rep_id] || [];
this.Repeaters[rep_id][cat_id] = this.Repeaters[rep_id][cat_id] || [];
if (-1 === this.Repeaters[rep_id][cat_id].indexOf(element_id)) {
this.Repeaters[rep_id][cat_id].push(element_id);
}
}
}
// How this code is meant to be called I think
RepeaterClass.collectRepeaterValues(0, 0, "dog");
RepeaterClass.collectRepeaterValues(0, 1, "dog");
RepeaterClass.collectRepeaterValues(0, 1, "cat");
// At the top level RepeaterClass.Repeaters is now an array structure
console.log(RepeaterClass.Repeaters.length); // 1
console.log(RepeaterClass.Repeaters[0][0][0]); // dog
console.log(RepeaterClass.Repeaters[0][1][0]); // dog
console.log(RepeaterClass.Repeaters[0][1][1]); // cat
The Only Solution I found is by create another array to store the elements inside the repeater then push it in the main Repeaters array outside the funtion
But Still cannot achieve it in the same function.
var RepeaterClass = {
Repeaters: {},
validated: {},
collectRepeaterValues: function( cat_id, element_id ) {
// this.Repeaters[ rep_id ] = this.Repeaters[ rep_id ] || [];
this.validated[ cat_id ] = this.validated[ cat_id ] || [];
if ( -1 === this.validated[ cat_id ].indexOf( element_id ) ) {
this.validated[ cat_id ].push( element_id );
}
}
AnotherFunction: function() {
_.each( REP, function( repDetails, repID ) {
_.each( repDetails, function( value ) {
/* 1. Call the Collector */
collectRepeaterValues( value['cat'], value['id'] );
} );
/* 2. push the validated output inside the main array */
this.Repeaters[ repID ] = this.Repeaters[ repID ] || [];
this.Repeaters[ repID ] = this.validated;
/* Empty for another session */
this.validated = {};
} );
}
}
Related
I have a react update form and i wish to differentiate the form data and the current data to figure out what has changed dynamically.
Problem summarized
find the minimum differences between 2 nested objects. And output an array of changed properties paths. E.g. if department_id in the departments list at index 0 changes while the rest stays the same - the algorithm should output ['departments'].
Example Data:
My data usually comes in like this (This is a simplified form , but the data has varying depths as shown here):
{id:115,
departments: [{
department_id:1,
department_name:"asd"}],
name: 'Test project',
}
Lets say the user decides to add in a department to the object, i wish to be able to detect the changes this way:
changes = ['departments']
or if the user changes the name :
changes = ['name']
the additional challenge here is that i wish to use this function across my forms , which means that the comparing should be able to handle different keys and depths of data
Edit:
data1 :
creation_date: "2020-06-16"
customer_information: Array(1)
0: 1
project_status: 1
sales_department: 1
sales_project_name: "helloss2sasdssssssssssss"
userProfile: Array(2)
0: 1
data2:
creation_date: "2020-06-16"
customer_information: Array(1)
0: 1
project_status: 1
sales_department: 1
sales_project_name: "helloss2"
userProfile: Array(2)
0: 1
1: 2
Function called here :
const data1 = action.original
const data2 = action.final
const difference = Object.keys(data1).filter((key)=>!walk(data1[key],data2[key]))
console.log(difference)
Here is the console log for difference :
[]
Expected:
['userProfile' , 'sales_project_name']
Simple naive recursive function walk that deep equals and returns if the branch has changes. filters keys that match.
data1 = {
creation_date: "2020-06-16",
customer_information: [1],
project_status: 1,
sales_department: 1,
sales_project_name: "helloss2sasdssssssssssss",
userProfile: [1],
version: 1
}
data2 = {
creation_date: "2020-06-16",
customer_information: [1],
project_status: 1,
sales_department: 1,
sales_project_name: "helloss2",
userProfile: [1, 2],
version: 2
}
walk = (node1, node2) => {
// different types, return false
if (typeof node1 !== typeof node2) return false
if (node1 && node2 && typeof node1 === 'object') {
const keys = Object.keys(node1)
// if type object, check same number of keys and walk on node1, node2
return keys.length === Object.keys(node2).length &&
keys.every(k => walk(node1[k], node2[k]))
}
// not object and types are same, return if node1 is equal to node2
return node1 === node2
}
console.log(
Object.keys(data1).filter((key) => !walk(data1[key], data2[key]))
)
If you don't want to use any library for comparing nested objects, you could simply convert to JSON and compare the strings.
Assuming you want to compare objects, it could look like this:
function getUnequalKeys(object1, object2) {
let unequalKeys = [];
for (let key in object1) {
if (object1.hasOwnProperty(key)
&& (
!object2.hasOwnProperty(key)
|| JSON.stringify(object1[key]) !== JSON.stringify(object2[key])
)
) {
unequalKeys.push(key);
}
}
for (let key2 in object2) {
if (object2.hasOwnProperty(key2) && !object1.hasOwnProperty(key2)) {
unequalKeys.push(key2);
}
}
return unequalKeys;
}
This would return all first-level keys that don't exist in both objects or have different values.
EDIT:
What it essentially does is the following:
Loop through each key in object1. Check if that same key exists in object2. If it does not exist, it means the key is not equal, so the condition is true and the key is added to the list of unequal keys. If the key does exist in object2, make a JSON string of both values and compare those strings. If the strings are not the same, it means they have different values. In that case, also add the key to the array of unequal keys.
Now we already checked all keys of object1.
As a last step, go through all keys of object2 and check if they are not present in object1, and in that case also add them to the array of unequal keys.
I have an array:
arr = [ 1, 2 , 3 ]
And another array where i hold DOM elements as
Elem = [ 1, 3]
I need to iterate over arr and only do stuff if the index match. For example since I have elem 1 and 3 when I loop through arr something should only happen for 1 and 3 and 2 should be skipped since there is no elem 2.
Someone told me to look into associative arrays and I wonder how I can do this with the least number of lines.
I want the code to be simple and readable and so far all the examples of associative arrays make no sense and are bloated.
for(var i = 0;i<arr.length;i++){
if(Elem.indexOf(arr[i])>-1){
//Elem contains arr[i] (contains object that at index i in arr)
//will be called only for 1 and 3 in arr
arr[i] = ... //do what you want with this object.
}
}
Do you mean this?
I modified the second array a bit to allow defining multiple actions in one place. I am not sure if I understand you correctly.
// array of DOM objects available
var arr = ['object1-selector', 'object2-selector', 'object3-selector'];
// array of actions with items that the method should be applied to
var actions = [
{
items: ['object1-selector', 'object3-selector'],
perform: function(elem) {
alert(elem);
}
},
{
items: ['object2-selector'],
perform: function(elem) {
alert(elem);
}
},
{
items: ['object4-selector'],
perform: function(elem) {
alert(elem);
}
}
];
//forEach loop that iterates over actions and checks if selector exists.
//If yes - it invokes the method
actions.forEach(function(action) {
action.items.forEach(function(item) {
if(arr.indexOf(item) > -1) {
action.perform(item);
}
});
});
If you want to have actions defined in one place and objects in a multidimensional array - let me know. I will try to adjust the example. If you don't store selectors but whole DOM objects, just modify the items: array and loop, that checks if element exists.
Oh, and here is jsfiddle: http://jsfiddle.net/3WJxc/2/. jQuery used only for alert() to show you working example.
Not really sure how you identify the elements in the second array but this is my suggestion. Array with ids
var arr = [ "id_1", "id_2", "id_3" ]
var Elem = {
"id_1": html_element,
"id_2": html_element,
"id_3": html_element
}
Then all you need to do is
for( var i = 0; i < arr.length; i++ ) {
if( Elem[ arr[i] ] ) {
// do stuff
}
}
Lets say I have this object here:
var items = [
{name:"Foo"},
{name:"Bar"},
{name:"foo"},
{name:"bar"},
{name:"foobar"},
{name:"barfoo"}
];
Since it only has one item in each object, I want to just return a list of them.
I tried this:
var getSingle = function(rows){
var items = [];
//This should only return one column
for (var i = 0; i < rows.length; i++) {
var r = rows[i];
var c = 0;
for (var n in r) {
if(c == 0)
items.push(r[n]);
c += 1;
}
}
return items;
}
But it doesn't seem to work. Any thoughts?
PS. name could be anything.
I used a different approach than the others, because I make two assumptions:
1/ you do not know the name of the key, but there is only one key for every item
2/ the key can be different on every item
I will give you a second option, with the second assumption as: 2/ all item have only one key but that's the same for all of them
First Options :
var items = [
{name:"Foo"},
{name:"Bar"},
{name:"foo"},
{name:"bar"},
{name:"foobar"},
{name:"barfoo"}
];
// object keys very simple shim
Object.keys = Object.keys || function(o) {
var result = [];
for(var name in o) {
if (o.hasOwnProperty(name))
result.push(name);
}
return result;
};
// function to get the value of every first keys in an object
// just remember that saying "first key" does not make real sense
// but we begin with the assumption that there IS ONLY ONE KEY FOR EVERY ITEM
// and this key is unknown
function getFirstKeysValues(items) {
var i = 0, len = items.length, item = null, key = null, res = [];
for(i = 0; i < len; i++) {
item = items[i];
key = Object.keys(item).shift();
res.push(item[key]);
}
return res;
}
console.log(getFirstKeysValues(items)); //["Foo", "Bar", "foo", "bar", "foobar", "barfoo"]
Second options will use a map, because we believe that every child possess the same key (I wouldn't use this one, because I do not like .map that much - compatibility):
var items = [
{name:"Foo"},
{name:"Bar"},
{name:"foo"},
{name:"bar"},
{name:"foobar"},
{name:"barfoo"}
];
// object keys very simple shim
Object.keys = Object.keys || function(o) {
var result = [];
for(var name in o) {
if (o.hasOwnProperty(name))
result.push(name);
}
return result;
};
// function to get the value of every first keys in an object
// just remember that saying "first key" does not make real sense
// but we begin with the asumption that there IS ONLY ONE KEY FOR EVERY ITEM
// and this key is unknown but the same for every child
function getFirstKeysValues(items) {
var key = items.length > 0 ? Object.keys(items[0]).shift() : null;
items = items.map(function (item) {
return item[key];
});
return items;
}
console.log(getFirstKeysValues(items));
This is usually accomplished using the map method, see the documentation here.
var justNamesArray = items.map(function(elem) { return elem.name});
The documenation page also includes a useful shim, that is a way to include it in your code to support older browsers.
Accompanying your request in the edit, if you would just like to get those that contain this property there is a nifty filter method.
var valuesWithNamePropert= items.filter(function(elem) { return elem.hasOwnProperty("name")});
You can chain the two to get
var justNamesWhereContains = items.filter(function(elem) { return elem.hasOwnProperty("name")}).
.map(function(elem) { return elem.name});
This approach (mapping and filtering), is very common in languages that support first order functions like JavaScript.
Some libraries such as underscore.js also offer a method that does this directly, for example in underscore that method is called pluck.
EDIT: after you specific that the property can change between objects in the array you can use something like:
var justReducedArray = items.map(function(elem) { for(i in elem){ return elem[i]}});
your var items = [] is shadowing your items parameter which already contains data. Just by seeing your code I thought that maybe your parameter should be called rows
If you're in a world >= IE9, Object.keys() will do the trick. It's not terribly useful for the Array of Objects, but it will help for the iteration of the Array (you would use Array.forEach to iterate the array proper, but then you would use the Object.keys(ob)[0] approach to get the value of the first property on the object. For example:
var someArr = [{ prop1: '1' },{ prop2: '2' },{ prop3: '3' }];
var vals = [];
someArr.forEach( function(obj) {
var firstKey = Object.keys(obj)[0];
vals.push(obj[firstKey]);
});
//vals now == ['1','2','3']
Obviously this isn't null safe, but it should get you an array of the values of the first property of each object in the original array. Say that 3 times fast. This also decouples any dependency on the name of the first property--if the name of the first property is important, then it's a trivial change to the forEach iteration.
You can override the Array.toString method for items, so using String(items) or alert(items) or items+='' will all return the string you want-
var items = [{name:"Foo"}, {name:"Bar"},{name:"foo"},
{name:"bar"},{name:"foobar"},{name:"barfoo"}];
items.toString= function(delim){
delim=delim || ', ';
return this.map(function(itm){
return itm.name;
}).join(delim);
}
String(items)
/* returned value: (String)
Foo, Bar, foo, bar, foobar, barfoo
*/
instead of the default string-'[object Object],[object Object],[object Object],[object Object],[object Object],[object Object]'
I am trying to implement a Trie in Javascript, which is easy enough but I seem to have hit a road block with my object.
The nodes are structured as follows:
var node = {
children: []
}
Children is an array of nodes that is mapped by a letter in a string. So the string "Test" would look like this:
root = {
children: [
't' => {
children: [
'e' => {
children: [
's' => {
children: [
't' => {
children: []
}
]
}
]
}
]
}
]
};
So each children array should have a length of 1, but if do something like alert(this._root.children.length); I get zero. Any thoughts on why this is happening?
Here is the rest of my implementation:
function Trie() {
this._root = {
children: []
};
}
Trie.prototype = {
//restore constructor
constructor: Trie,
add: function (str){
var curr = this._root,
prev,
currchar;
// For each character in the string
for(var i = 0, j = str.length; i < j; i++) {
// Insert only lowercase letters for efficiency
currchar = str.toLowerCase().charAt(i);
prev = curr;
curr = prev.children[currchar];
// Traverse until we hit a non-existant node
if(typeof(curr) == "undefined") {
// Make a new node
prev.children[currchar] = {
children: []
};
curr = prev.children[currchar];
}
}
}
You are adding properties to the array instance object, not elements to the array. The length property only includes array elements, not properties on the array instance object.
var a = [23, 42];
console.log(a.length); // 2
a['foo'] = 'bar';
console.log(a.length); // 2
a[2] = 1337;
console.log(a.length); // 3
EDITED:
You could instead structure the nodes like this:
var node = {
children: {},
length: function () {
var i = 0;
var k;
for (k in this.children) {
if (this.children.hasOwnProperty(k)) {
i++;
}
}
return i;
}
};
This is inefficient, of course. You should instead define a Node class with the length method on its prototype. Alternatively, define an add method that updates the length property.
I think that the problem is that you use a javasrcipt array as an associative array (as found in other languages). In javascript "associative" arrays are objects that don't have a length property. Normal arrays have numeric indices.
Irrelevant to the question but you might find this useful.
Maybe you want
str.toLowerCase().charCodeAt(i)
instead of
str.toLowerCase().charAt(i)
If str is "f1", the properties you're adding to the children array are "f" and "1" which should cause an array with property named f and length 0, and another child array with length 2 and property 1.
To get only numeric properties, you should make sure your property names are valid array indices -- positive integers representable in 31 bits.
By using charCodeAt instead of charCode, you would get the property names 102 and 49 instead of "f" and 1.
My question is related to this question. You will have to first read it.
var ids = "1*2*3";
var Name ="John*Brain*Andy";
var Code ="A12*B22*B22";
Now that I have an array of javascript objects. I want to group my objects based on CODE. So there can be duplicate codes in that code string.
As per the above changed strings, I have same code for Brain and Andy. So, now I want two arrays. In one there will be only one object containing details of only John and in the other object there will be two objects containing details of Brain and Andy.
Just for example I've taken 3 items. In actual there can be many and also there can be many set of distinct codes.
UPDATE
I needed the structure like the one built in groupMap object by the #Pointy. But I will use #patrick's code to achieve that structure. Many thanks to both of them.
It is a little hard to tell the exact resulting structure that you want.
This code:
// Split values into arrays
Code = Code.split('*');
Name = Name.split('*');
ids = ids.split('*');
// cache the length of one and create the result object
var length = Code.length;
var result = {};
// Iterate over each array item
// If we come across a new code,
// add it to result with an empty array
for(var i = 0; i < length; i++) {
if(Code[i] in result == false) {
result[ Code[i] ] = [];
}
// Push a new object into the Code at "i" with the Name and ID at "i"
result[ Code[i] ].push({ name:Name[i], id:ids[i] });
}
Will produce this structure:
// Resulting object
{
// A12 has array with one object
A12: [ {id: "1", name: "John"} ],
// B22 has array with two objects
B22: [ {id: "2", name: "Brain"},
{id: "3", name: "Andy"}
]
}
Split the strings on "*" so that you have 3 arrays.
Build objects from like-indexed elements of each array.
While building those objects, collect a second object that contains arrays for each "Code" value.
Code:
function toGroups(ids, names, codes) {
ids = ids.split('*');
names = names.split('*');
codes = codes.split('*');
if (ids.length !== names.length || ids.length !== codes.length)
throw "Invalid strings";
var objects = [], groupMap = {};
for (var i = 0; i < ids.length; ++i) {
var o = { id: ids[i], name: names[i], code: code[i] };
objects.push(o);
if (groupMap[o.code]) {
groupMap[o.code].push(o);
else
groupMap[o.code] = [o];
}
return { objects: objects, groupMap: groupMap };
}
The "two arrays" you say you want will be in the "groupMap" property of the object returned by that function.