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
}
}
Related
I am using a kind of dict in javascript and want to add an element to a list which is part of a kind of dictionary.
Here is the code snippet:
lines = [
[1,2],
[2,4],
[2,3],
[3,5]
];
nodes = [1,2,3,5,4];
function get_adjdict(nodes, lines) {
// create an empty something
adjacent = [];
// loop over all elements on the array 'nodes'. The variable 'node' is supposed to take the various values of the elements in 'nodes'. So in this example this will be the values 1,2,3,5,4.
for (var node in nodes) {
// Add a key-value pair to the object/array/whatever named 'adjacent'. key is the value of 'node, the value is an empty array.
adjacent.push({node:[]});
// loop over all elements on the array 'lines'. The variable 'line' is supposed to take the various values of the elements in 'lines'. So in this example this will be the values [1,2], then [2,4] and so on
for (var line in lines) {
// checks if the value of 'node' is present in the array 'line'
if (line.includes(node)) {
// If the first element of the array 'line' has the same value as 'node'...
if (line[0] == node) {
// ... add the second element of 'line' to 'adjacent[node]'
adjacent[node].push(line[1]) //ERROR
} else {
// ... add the first element of 'line' to 'adjacent[node]'
adjacent[node].push(line[0])
}
}
}
}
return adjacent
}
The error is "TypeError: adjacent[node].push is not a function". How to do it then?
Expected data-structure:
adjdict = {
1: [2],
2: [1,4,3],
3: [2,5],
4: [2],
5: [3]
}
This what you are looking for:
var lines = [
[1,2],
[2,4],
[2,3],
[3,5]
];
var nodes = [1,2,3,4,5];
function get_adjdict (nodes, lines) {
var adjacent = {};
var node, line;
for (var node_idx in nodes) {
node = nodes[node_idx];
adjacent[node] = [];
for (var line_idx in lines) {
line = lines[line_idx];
if (line.includes(node)) {
if (line[0] == node) {
adjacent[node].push(line[1]);
} else {
adjacent[node].push(line[0]);
}
}
}
}
return adjacent;
}
get_adjdict(nodes, lines);
Bear in mind that, when using the construction for (var idx in arr) {} in JavaScript, idx is the key in the iteration, not the value.
for (var node in nodes) {
In the above code, node takes values 0 to 4. nodes[node] would take values 1 to 5 as I think you are expecting.
I always use the suffix _idx for this kind of variables. In this case, rename node to node_idx or node_index and you will see how everything falls into place.
You could just iterate the lines and create the object.
function add(o, k, v) {
if (!o[k]) {
o[k] = [];
}
if (o[k].indexOf(v) === -1) {
o[k].push(v);
}
}
var lines = [[1, 2], [2, 4], [2, 3], [3, 5]],
result = {};
lines.forEach(function (l) {
add(result, l[0], l[1]);
add(result, l[1], l[0]);
});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can simplify it with first mapping all of the indexes first and than use a simple reduce to build the object.
const lines = [
[1,2],
[2,4],
[2,3],
[3,5]
];
const nodes = [1,2,3,4,5];
// Make a lookup table where all the numbers appear
var lookup = lines.slice(0).reduce( (o, node, i) => {
o[node[0]] = o[node[0]] || []; // if we have not found it, set an array
o[node[0]].push(node[1]); // add index value to the array
o[node[1]] = o[node[1]] || []; // if we have not found it, set an array
o[node[1]].push(node[0]); // add index value to the array
return o //return object for reduce
}, {})
var result = nodes.reduce( (o, n) => { //now loop over the nodes and make the `hash` table
o[n] = lookup[n] || []
return o
}, {})
console.log(result)
Don't use for/in loops on arrays. They can wind up iterating inherited properties as well as array items. for/in is for object iteration. Use .forEach() instead which will make working with the enumerated items much simpler (no indexes to manage).
Next, you are indicating that you want an object outputted, but you are creating adjacent as an array. Arrays inherit from objects, but they store their data differently.
Also, remember to formally declare your variables, otherwise they become global.
Lastly, don't rely on automatic semi-colon insertion. That can lead to bugs in certain edge cases.
If you follow good coding best practices, a lot of these kinds of issues just go away.
// Don't forget to use "var" to declare your variables otherwise
// they will become global.
var nodes = [1,2,3,4,5];
var lines = [
[1,2],
[2,4],
[2,3],
[3,5]
];
function get_adjdict(nodes, lines) {
var adjacent = {}; // <-- You are asking for an object, not an array
// Loop through the nodes array (use .forEach() to loop arrays)
nodes.forEach(function(node){
// Array to store results in
var results = [];
// Loop through the lines
lines.forEach(function(line) {
if (line.includes(node)) {
if (line[0] === node) {
results.push(line[1]);
} else {
results.push(line[0]);
}
// Set array as new property value
adjacent[node] = results;
}
});
});
// This needs to be outside of all loops, just before function terminates
return adjacent;
}
console.log(get_adjdict(nodes, lines));
I have an array of objects which contain certain duplicate properties: Following is the array sample:
var jsonData = [{x:12, machine1: 7}, {x:15, machine2:7},{x:12, machine2: 8}];
So what i need is to merge the objects with same values of x like the following array:
var jsonData = [{x:12, machine1:7, machine2:8}, {x:15, machine2:7}]
I like the lodash library.
https://lodash.com/docs#groupBy
_.groupBy(jsonData, 'x') produces:
12: [ {x=12, machine1=7}, {x=12, machine2=8} ],
15: [ {x=15, machine2=7} ]
your desired result is achieved like this:
var jsonData = [{x:12, machine1: 7}, {x:15, machine2:7},{x:12, machine2: 8}];
var groupedByX = _.groupBy(jsonData, 'x');
var result = [];
_.forEach(groupedByX, function(value, key){
var obj = {};
for(var i=0; i<value.length; i++) {
_.defaults(obj, value[i]);
}
result.push(obj);
});
I'm not sure if you're looking for pure JavaScript, but if you are, here's one solution. It's a bit heavy on nesting, but it gets the job done.
// Loop through all objects in the array
for (var i = 0; i < jsonData.length; i++) {
// Loop through all of the objects beyond i
// Don't increment automatically; we will do this later
for (var j = i+1; j < jsonData.length; ) {
// Check if our x values are a match
if (jsonData[i].x == jsonData[j].x) {
// Loop through all of the keys in our matching object
for (var key in jsonData[j]) {
// Ensure the key actually belongs to the object
// This is to avoid any prototype inheritance problems
if (jsonData[j].hasOwnProperty(key)) {
// Copy over the values to the first object
// Note this will overwrite any values if the key already exists!
jsonData[i][key] = jsonData[j][key];
}
}
// After copying the matching object, delete it from the array
// By deleting this object, the "next" object in the array moves back one
// Therefore it will be what j is prior to being incremented
// This is why we don't automatically increment
jsonData.splice(j, 1);
} else {
// If there's no match, increment to the next object to check
j++;
}
}
}
Note there is no defensive code in this sample; you probably want to add a few checks to make sure the data you have is formatted correctly before passing it along.
Also keep in mind that you might have to decide how to handle instances where two keys overlap but do not match (e.g. two objects both having machine1, but one with the value of 5 and the other with the value of 9). As is, whatever object comes later in the array will take precedence.
const mergeUnique = (list, $M = new Map(), id) => {
list.map(e => $M.has(e[id]) ? $M.set(e[id], { ...e, ...$M.get(e[id]) }) : $M.set(e[id], e));
return Array.from($M.values());
};
id would be x in your case
i created a jsperf with email as identifier: https://jsperf.com/mergeobjectswithmap/
it's a lot faster :)
I need to remove all elements in an array that do not contain "IN" in uppercase exactly like that.
How I thought of doing this was to traverse the array with a for loop and write all values that contain IN to another array.
Is there a way I can do it without writing to a new array and just removing those items that don't match from the current array?
Here is the code for how I was planning on doing it:
arrTwo = [];
for(var i = 0; i<arr.length; i++){
if(arr[i].indexOf('IN') > -1) arrTwo.push[arr[i]];
}
You can use ES5 filter method:
arr = arr.filter(function(s){
return ~s.indexOf("IN");
});
And using ES6 arrow functions, it can be simplified to:
arr = arr.filter(s=>~s.indexOf("IN"));
Here's a really good thread that has a couple of ways to accomplish this. If you do not delete the element of the array in the correct manner, you that element will be undefined rather than actually deleted. The .spilce() method is what you want to look into.
Deleting array elements in JavaScript - delete vs splice
I would do it using the splice() method:
var testArray = [ 'this one contains IN', 'this one does not' ];
function filterArray ( arr ) {
var i = arr.length;
//-- Loop through the array in reverse order since we are modifying the array.
while (i--) {
if (arr[i].indexOf('IN') < 0) {
//-- splice will remove the non-matching element
arr.splice(i, 1);
}
}
}
filterArray( testArray );
document.body.innerText = JSON.stringify(testArray);
JSFiddle: http://jsfiddle.net/5DW8L/1/
I found a solution to where I get returned an array of elements without duplicates:
Array1 = Array1.filter(function(val) {
return Array2.indexOf(val) == -1;
});
However, I want to modify this code just a little bit. Instead of being returned an array without duplicates, I want to do something when there is a duplicate. The problem is, I'm not sure how exactly this code works. The thing is I'm not sure how val gets set, or what it even is.
for (var i = 0; i < json.length; i++) {
var item = json[i];
// if json.indexOf(val?), do something
}
Read the docs for the Array filter method then. The val parameter of the callback will be passed the single array items, i.e. json[i] or item in your case:
for (var i = 0; i < json.length; i++) {
var item = json[i];
if (json.indexOf(item) >= 0) {
// do something
}
}
var newArray = array1.filter(function(v, i) {
return array1.indexOf(v) == i;
});
This will return only unique itesm from array1;
array1.filter(function(v, i) {
// write your code here ('v' is individual value and 'i' is its index)
// don't return any anything if you don't want unique array to be returned.
// 'array1.indexOf(v) == i' checks if current value is duplicate from previous any values.
// try putting console.log on values you don't understand like (console.log(v,i) for values of 'v' and 'i')
return array1.indexOf(v) == i;
});
and off-curse you can loop an array with for loop as
for(i in array1){
// where i is index of array1, to get current value use array1[i]
if(array2.indexOf(array1[i]) >= 0){
// do something
}
console.log(i);
}
val is set by Array.prototype.filter, which calls the callback function on each element in the array. Since you don't want to filter you can use Array.prototype.forEach instead, which also calls the callback function once for each element in the array:
Array1.forEach(
// This function is called once per element in Array1
function(val){
if(Array2.indexOf(val) != -1){ // Check if that element is also in Array2
// `val` is in both arrays,
// Do something with it
}
}
);
You can utilize some modern libraries... like underscorejs.
Intersection is what you're looking for i guess: http://underscorejs.org/#intersection
_.intersection([1, 2, 3], [101, 2, 1, 10], [2, 1]);
=> [1, 2]
So your code may be something like
if(_.insersection(arr1, arr2)){
//since [] array is Falsy in JS this will work as a charm
}
From MDN: indexOf
Returns the first index at which a given element can be found in the array, or -1 if it is not present.
From MDN: filter
Creates a new array with all elements that pass the test implemented by the provided function.
The first function works by returning true when an item from array1 isn't found in array2 (== -1). i.e.: Iterate through A and add anything not found in B.
So, to change to return only duplicates return true for anything that is found in both:
Array1 = Array1.filter(function(val) {
return Array2.indexOf(val) >= 0;
});
Array1 now contains only items with duplicates.
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.