I have one array that has objects whose keys match another array of objects with the same keys, and the value for the first key is a Month index (0 = January, 1 = February, etc.) and the points may cross a year division (a:10, a:11, a:0, a:1)
However the first array may not have the same number of objects, and the values for the key on one object may not exist in the other object on the same key, and vice-versa.
Assuming both arrays are already ordered correctly, I want to compare those two arrays, and if one array is missing an object with a value for a key that the other one has, I want to add a new object to the first array with the same key and value, in the same position/index within the array that doesn't have it, as it is in the array that does have it.
let arr1 = [{a:0, b:1},{a:1, b:3},{a:3, b:18},{a:4, b:2}]
let arr2 = [{a:10, b:2},{a:11, b:4},{a:0, b:8},{a:1, b:5},{a:2, b:1}]
arr1 is missing objects with an a value of 10, 11, and 2 that exist in arr2, and arr2 is missing objects with an a value of 3 and 4 that exist in arr1
What I want to end up with is:
arr1 = [{a:10, b:0},{a:11, b:3},{a:0, b:1},{a:1, b:3},{a:2, b:0},{a:3, b:18},{a:4, b:2}]
arr2 = [{a:10, b:2},{a:11, b:4},{a:0, b:8},{a:1, b:5},{a:2, b:1},{a:3, b:0},{a:4, b:0}]
Now arr1 has new items/objects for a:10, a:11, and a:2 while
arr2 has new items for a:3 and a:4, all of which have a b value of 0;
I've tried plotting this out on paper to see logically what I would do physically, but I just can't get my head around it, so a "For Dummies" answer would be really helpful. I thought I had learned enough to be employable, but this is really sticking me up, and it's just for a simple HTML5 canvas line graph. I'm getting data from a DB to compare two lines on the same graph, where there may not be data for one month for one type of data, but there is for the other type of data. And for those spots that don't exist in one or the other, I want to drop the line down to 0 on the Y axis, then back up to the next value. https://github.com/rmgreenstreet/custom-forms/blob/master/public/javascripts/canvasRender.js
Having just month indexes is not OK for proper sorting.
Just add some information about year and it will be done easily.
Without sorting it may look like:
// loop elements of the arr2 with reduce,
// if there is any element in arr1 with the same value of key 'a',
// result is the same as on the previous step
// if there is no elements of that kind, add new object {'a': arr2['a'], 'b' : 0} into arr1
function newArray (arr1, arr2) {
return arr2.reduce((result, obj2) => {
if (arr1.some(obj1 => obj1['a'] === obj2['a'])) {
return result;
}
return [...result, {['a'] : obj2['a'], 'b':0}];
}, arr1)
}
// now you can assign the result of newArray() to new variables
const arr1_ = newArray(arr1, arr2)
const arr2_ = newArray(arr2, arr1)
OP's final working function (having changed the a value to be a mm/yyyy string:
function equalize(arr1, arr2) {
let newArr = arr2.reduce(function (result, obj2) {
if (arr1.some(obj1 => obj1['a'] === obj2['a'])) {
return result;
}
return [...result, {'a' : obj2['a'], 'b':0}];
}, arr1);
newArr.sort(function (a, b) {
console.log(`a: ${a}`)
a = a.x.split('/');
b = b.x.split('/')
return new Date(a[1], a[0], 1) - new Date(b[1], b[0], 1)
});
return newArr;
};
The main annoyance of this task is checking for presence of items with certain values of a. A naive straightfoward solution would require iterating over arr2 for evey item of arr1 and vice versa, which makes it O(n2), i.e. slow.
An alternative approach suggests employing objects as fast lookup maps. The idea is to turn your array inside out, use as as keys and bs as values.
let arr1 = [{a:1, b:1},{a:2, b:3},{a:4, b:18},{a:5, b:2}]
let arr2 = [{a:2, b:2},{a:3, b:4},{a:4, b:8},{a:6, b:5},{a:7, b:1}]
// Using objects as lookup maps.
let m1 = {}
let m2 = {}
// Filling the maps.
// The 'a' becomes a key, the 'b' becomes a value.
arr1.forEach(v => m1[v.a] = v.b)
arr2.forEach(v => m2[v.a] = v.b)
// Iterating over the keys of m1, checking if m2 has that key,
// if not - adding it with a value of 0.
for (let f in m1) m2[f] || (m2[f] = 0)
// The same goes for m2 -> m1.
for (let f in m2) m1[f] || (m1[f] = 0)
// At this point both m1 & m2 have all the keys from both arrays without gaps.
let res1 = []
let res2 = []
// Assembling the resulting arrays.
for (let f in m1) res1.push({a: f, b: m1[f]})
for (let f in m2) res2.push({a: f, b: m2[f]})
Pardon my inclination for one-liners.
Here is my solution. I am using lodash for helper functions.
function combineArrays (a, b) {
Object.keys(b).forEach(function (k) {
const key = parseInt(k);
if (!a[key]) {
a[key] = b[key];
a[key].b = 0;
}
});
return _.values(a);
}
Working code snippet
// const arr1 = [{ a: 1, b: 1 }, { a: 2, b: 3 }, { a: 4, b: 18 }, { a: 5, b: 2 }];
// const arr2 = [{ a: 2, b: 2 }, { a: 3, b: 4 }, { a: 4, b: 8 }, { a: 6, b: 5 }, { a: 7, b: 1 }];
let arr1 = [{a:0, b:1},{a:1, b:3},{a:3, b:18},{a:4, b:2}]
let arr2 = [{a:10, b:2},{a:11, b:4},{a:0, b:8},{a:1, b:5},{a:2, b:1}]
const arr1Map = _.keyBy(arr1, 'a');
const arr2Map = _.keyBy(arr2, 'a');
function combineArrays(a1Map, a2Map) {
const a = _.cloneDeep(a1Map);
const b = _.cloneDeep(a2Map);
Object.keys(b).forEach(function(k) {
const key = parseInt(k);
if (!a[key]) {
a[key] = b[key];
a[key].b = 0;
}
});
return _.values(a);
}
console.log(combineArrays(arr1Map, arr2Map));
console.log(combineArrays(arr2Map, arr1Map));
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.15/lodash.min.js"></script>
You should loop through the first array, and check if each key is in the second array. If it's not you should insert an item using arr.splice().
If you know both lists are sorted, ie the keys are in order, then you can also don't need to check the whole array for the new key.
let j = 0;
for (let i = 0; i < arr1.length; i++) {
let possiblyMissingKey = arr1[i].a;
while (arr2[j].a < possiblyMissingKey && j < arr2.length)
j++;
if (arr2[j].a != possiblyMissingKey) {
let itemToInsert = {a:possiblyMissingKey, b:0};
arr2.splice(j, 0, itemToInsert);
}
}
After you go through the first array, do the second array.
I have two array. I want to merge this two arrays into one array. One array consisting keys and another one values.My array looks like
productId = [8,7,9];//Key Element
quantity = ["5","1","3"];//Value Element
//expected new array
newarray = {
"8": 5,
"7": 1,
"9": 3
}
I already tried to merge these arrays, in this way
var newArray = {};
for(var i=0; i< productId.length; i++){
newArray[productId[i]] = quantity [i];
}
console.log(newArray);
It returns
Object [ <7 empty slots>, "5", "1", "3" ]
You are working in firefox so you may get this type of issue because the problem might be caused at how Firefox' console.log has interpreted the input object.
Please look here
Empty slots in JavaScript objects?
Try this
var productId = [8,7,9];
var quantity = ["5","1","3"];
var newarray = {};
productId.forEach((key, i) => newarray[key] = quantity[i]);
console.log(newarray);
Try the following:
var productId = [8,7,9];//Key Element
var quantity = ["5","1","3"];//Value Element
var obj = {};
var i = 0;
for(var k of productId) {
obj[k] = parseInt(quantity[i]);
i++;
}
console.log(obj);
Your new "array" is not an Array but an Object.
You can iterate on one of the arrays using Array.reduce to construct the object.
Something like that:
const arr1 = ['8', '2', '4'];
const arr2 = ['28', '12', '45'];
const result = arr1.reduce((obj, currentItem, index) => {
obj[currentItem] = arr2[index];
return obj;
}, {});
console.log(result);
I have an array
sourceArray = [{'type':'A'}, {'type':'B'}, {'type':'C'}, {'type':'D'}];
arrayB = ['B', 'C'];
I want to filter array sourceArray from values which arrayB contains.
We can do this by iterating arrayB, but just want some good way to do this.
filteredArray = [];
for(x in arrayB)
{
filteredArray.concat( sourceArray.filter(function(e1){ return e1.type == arrayB[x])} );
}
can be have any way to do this more gracefully.
Just .filter it:
sourceArray = [{'type':'A'}, {'type':'B'}, {'type':'C'}, {'type':'D'}];
arrayB = ['B', 'C'];
result = sourceArray.filter(function(item) {
return arrayB.indexOf(item.type) >= 0;
});
document.write("<pre>" + JSON.stringify(result,0,3));
[].filter(func) iterates an array and collects elements for which func returns true. In our function, we check whether arrayB contains item.type and return true if it does (see indexOf).
ES6 solution, for those who already use it:
sourceArray = [{'type':'A'}, {'type':'B'}, {'type':'C'}, {'type':'D'}];
arrayB = ['B', 'C'];
setB = new Set(arrayB)
result = sourceArray.filter(item => setB.has(item.type))
There's the solution of filtering and using indexOf, but it contains a hidden iteration which is costly if your arrayB array contains more than just a few elements.
In the general case, the efficient solution is to build a hash map of the elements so that the filtering operation is faster. This can be done like this:
var filteredArray = sourceArray.filter(
function(v){ return this[v.type] }.bind(arrayB.reduce(
function(s,v){ s[v]=1; return s }, Object.create(null)
))
)
In this code arrayB.reduce(function(s,v){ s[v]=1; return s }, {})) is an object whose keys are the valid types : {B: 1, C: 1}. JavaScript engines are very fast at repetitively retrieving the properties of such an object.
var sourceArray = [{
'type': 'A'
}, {
'type': 'B'
}, {
'type': 'C'
}, {
'type': 'D'
}];
var arrayB = ['B', 'C'];
var desiredArr = sourceArray.filter(function (val) {
for (var i = 0; i <= arrayB.length; ++i) {
if (val.type == arrayB[i]){
return val;
}
}
});
alert(JSON.stringify(desiredArr));
i have this variable below,
var arr = {
lines: 1,
angle: 2
};
how would i append / push this into my arr variable?
var arr2 = { hi : 3 }
I've tried push() and just simply adding the two. but failed
var r = arr+''+arr2;
alert(r.toSource());
the alert gives me this
(new String("[object Object][object Object]"))
you can add a new property in a static way, like this:
arr.hi = 3;
but if you want to add all the properties from another object (a merge of object's properties) here a simple snippet:
var arr = {
lines: 1,
angle: 2
};
alert(JSON.stringify(arr));
var arr2 = {
hi : 3,
jsIs: "aWeSoMe"
};
//add all the key/value pairs of arr2 to arr
Object.keys(arr2).forEach(function (key) {
arr[key] = arr2[key];
});
alert(JSON.stringify(arr));
You can assign that into arr variable using
// if you want to enter object itself
arr.arr2 = arr2;
// if you want to remove arr2
delete arr2
// if you want to include just hi
arr.hi = arr2.hi;
// if you want to remove hi from arr2
delete arr2.hi
I've created an array in JavaScript and inserted objects with keys of object_ids:
var ar = [];
ar[4] = 'a';
ar[2] = 'b';
ar[8] = 'c';
ar[5] = 'd';
Problem is when I print this out the array I get is:
[undefined, undefined, "b", undefined, "a", "d", undefined, undefined, "c"]
and
ar.length = 9
How do I prevent the array from auto filling with undefined values and simply save this array as a 4-element array?
Iteration over an array is not what I expect here.
Thanks!
You can use an object literal instead of an array
var ar = {};
ar[4] = 'a';
ar[2] = 'b';
ar[8] = 'c';
ar[5] = 'd';
// {"2":"b","4":"a","5":"d","8":"c"}
You can iterate like this:
for (var i in a) {
console.log(a[i]);
}
Here's what you are doing:
var ar = [];
ar[8] = 'c'; // creates [undefined, undefined, undefined, undefined, undefined, undefined, undefined, 'c'];
I believe this is what you want:
var ar = {};
ar[4] = 'a';
ar[2] = 'b';
ar[8] = 'c';
ar[5] = 'd';
Object.keys(ar).length == 4; // true
More information on Object.keys
I think you are confusing the behavior of JavaScript Arrays with the associative-array behavior of all JavaScript objects. Try this instead:
var a = {};
a[4] = 'a';
a[2] = 'b';
a[8] = 'c';
a[5] = 'd';
a; // {'2':'b', '4':'a', '8':'c', '5':'d'}
Note that the key/value pairs in an object are not ordered in any way. To order the keys or values you must maintain your own array of ordering.
what you want to do is probably:
array = {}
array["1"] = "b"
array["7"] = "aaa"
now array is:
Object { 1="a", 7="b"}
is it right?
var arr = {
0 : "hello",
8 : "world",
14: "!"
};
var count = 0;
for (var k in arr) {
++count;
}
document.write(arr[0] + " " + arr[8] + arr[14] + " with a length of " + count );
Outputs hello world! with a length of 3
You can use an Object.. and here is how to collect the count (if you needed)
Fiddle
There is no guarantee that iterating an Object using the for...in statement be in any specific order. The behavior is undefined in ECMA Script specification, quote:
The mechanics of enumerating the properties ... is implementation dependent.
Chrome doesn't iterate elements in order in many cases, last time I checked Opera went the same way and just now I read here that IE9 has adopted the same behavior. So your only solution that is guaranteed to keep both the association and order of elements is to use an Object to store keys and values and an Array to store the order:
var obj = { 4: 'a', 2: 'b', 8: 'c', 5: 'd' };
var order = [ 4, 2, 8, 5 ];
for( var i in order ) {
//do something with obj[ order[i] ]
}