Merge Object & Array: Javascript, JQuery - javascript

Illustrative example:
d1 = {
"ean_code": ["OA13233394CN08", "8903327046534", "8903327014779"],
"balance_qty": [5, 10, 15]
}
And
d2 = {
"ean_code": ["OA13233394CN11", "OA13233394CN08", "8903327014779", "OA13233394CN09"],
"scanned_qty": [30, 5, 20, 10, - 1],
}
Output:
d3 = {
"ean_code": ["OA13233394CN08", "8903327046534", "8903327014779", "OA13233394CN11", "OA13233394CN09"],
"scanned_qty": [5, 0, 20, 30, 10],
"balance_qty": [5, 10, 15, 0, 0]
}
Explaination. d3['scanned_qty'][1] default value is 0, because value of d3['ean_code'][1] is belongs to d1['ean_code'] array and d1 object doesn't have scanned_qty key.
Best possible way to do this operation?

You just need a custom solution for your specific case.
Merge 2 objects with no sub-objects (no recursion required)
Final object's array fields must be the same length
Final object's array fields must preserve index coherency
Final object's array fields must use '0' as a default value
http://jsfiddle.net/8X5yB/4/
function customMerge(a, b, uniqueKey) {
var result = {};
var temp = {};
var fields = {};
// object 1
for(var x=0; x<a[uniqueKey].length; x++) {
id = a[uniqueKey][x];
if(temp[id] == null) temp[id] = {};
for(k in a) {
if(k != uniqueKey) {
fields[k] = '';
temp[id][k] = (a[k].length > x ? a[k][x] : 0);
}
}
}
// object 2
for(var x=0; x<b[uniqueKey].length; x++) {
id = b[uniqueKey][x];
if(temp[id] == null) temp[id] = {};
for(k in b) {
if(k != uniqueKey) {
fields[k] = '';
temp[id][k] = (b[k].length > x ? b[k][x] : 0);
}
}
}
// create result
result[uniqueKey] = [];
for(f in fields) result[f] = [];
for(k in temp) {
result[uniqueKey].push(k);
for(f in fields) {
result[f].push(temp[k][f] != null ? temp[k][f] : 0);
}
}
return result;
}
...
var obj = customMerge(d1, d2, "ean_code");

Let's assume you have o1 and o2 as object 1 and 2, respectively.
var key,
result = {}
i,
largestLength = 0,
copyIntoResult = function (obj, key) {
for (i = 0; i < obj[key].length; i += 1) {
if (result[key].indexOf(obj[key][i]) === -1) {
result[key].push(obj[key][i]);
}
}
};
for (key in o1) {
if (o1.hasOwnProperty(key) && o2.hasOwnProperty(key)) {
result[key] = [];
copyIntoResult(o1, key);
copyIntoResult(o2, key);
if (result[key].length > largestLength) {
largestLength = result[key].length;
}
} else if (o1.hasOwnProperty(key)) {
result[key] = [].concat(o1[key]);
if (o1[key].length > largestLength) {
largestLength = o1[key].length;
}
}
}
for (key in o2) {
if (o2.hasOwnProperty(key) && !result[key]) {
result[key] = [].concat(o2[key]);
if (o2[key].length > largestLength) {
largestLength = o2[key].length;
}
}
}
// result now has the merged result
for (key in result) {
if (result[key].length < largestLength) {
for (i = 0; i < (largestLength - result[key].length); i += 1) {
result[key].push('');
}
}
}
EDIT: Upon the edit to your question, you can have all the arrays be the same length by equalizing the arrays to the maximum array length of the merged result. However, the default "blank" entry is up to you (in this case, I just used an empty string).

function merge(a,b) {
var c = {};
for(key in a.keys()) {
c[key] = a[key].slice(0);
}
for(key in b.keys()) {
if(typeof c[key] == 'undefined') {
c[key] = b[key].slice(0);
} else {
var adds = b[key].filter(function(item){
return (a[key].indexOf(item) == -1);
});
c[key].concat(adds);
}
}
return c;
}
.keys() method since v1.8.5, snippet for older browsers.
filter since v1.6, snippet for older browsers.
concat since v1.2.

Related

from an array of objects how do I find which value comes up most often, in javascript? [duplicate]

I'm looking for an elegant way of determining which element has the highest occurrence (mode) in a JavaScript array.
For example, in
['pear', 'apple', 'orange', 'apple']
the 'apple' element is the most frequent one.
This is just the mode. Here's a quick, non-optimized solution. It should be O(n).
function mode(array)
{
if(array.length == 0)
return null;
var modeMap = {};
var maxEl = array[0], maxCount = 1;
for(var i = 0; i < array.length; i++)
{
var el = array[i];
if(modeMap[el] == null)
modeMap[el] = 1;
else
modeMap[el]++;
if(modeMap[el] > maxCount)
{
maxEl = el;
maxCount = modeMap[el];
}
}
return maxEl;
}
There have been some developments in javascript since 2009 - I thought I'd add another option. I'm less concerned with efficiency until it's actually a problem so my definition of "elegant" code (as stipulated by the OP) favours readability - which is of course subjective...
function mode(arr){
return arr.sort((a,b) =>
arr.filter(v => v===a).length
- arr.filter(v => v===b).length
).pop();
}
mode(['pear', 'apple', 'orange', 'apple']); // apple
In this particular example, should two or more elements of the set have equal occurrences then the one that appears latest in the array will be returned. It's also worth pointing out that it will modify your original array - which can be prevented if you wish with an Array.slice call beforehand.
Edit: updated the example with some ES6 fat arrows because 2015 happened and I think they look pretty... If you are concerned with backwards compatibility you can find this in the revision history.
As per George Jempty's request to have the algorithm account for ties, I propose a modified version of Matthew Flaschen's algorithm.
function modeString(array) {
if (array.length == 0) return null;
var modeMap = {},
maxEl = array[0],
maxCount = 1;
for (var i = 0; i < array.length; i++) {
var el = array[i];
if (modeMap[el] == null) modeMap[el] = 1;
else modeMap[el]++;
if (modeMap[el] > maxCount) {
maxEl = el;
maxCount = modeMap[el];
} else if (modeMap[el] == maxCount) {
maxEl += "&" + el;
maxCount = modeMap[el];
}
}
return maxEl;
}
This will now return a string with the mode element(s) delimited by a & symbol. When the result is received it can be split on that & element and you have your mode(s).
Another option would be to return an array of mode element(s) like so:
function modeArray(array) {
if (array.length == 0) return null;
var modeMap = {},
maxCount = 1,
modes = [];
for (var i = 0; i < array.length; i++) {
var el = array[i];
if (modeMap[el] == null) modeMap[el] = 1;
else modeMap[el]++;
if (modeMap[el] > maxCount) {
modes = [el];
maxCount = modeMap[el];
} else if (modeMap[el] == maxCount) {
modes.push(el);
maxCount = modeMap[el];
}
}
return modes;
}
In the above example you would then be able to handle the result of the function as an array of modes.
Based on Emissary's ES6+ answer, you could use Array.prototype.reduce to do your comparison (as opposed to sorting, popping and potentially mutating your array), which I think looks quite slick.
const mode = (myArray) =>
myArray.reduce(
(a,b,i,arr)=>
(arr.filter(v=>v===a).length>=arr.filter(v=>v===b).length?a:b),
null)
I'm defaulting to null, which won't always give you a truthful response if null is a possible option you're filtering for, maybe that could be an optional second argument
The downside, as with various other solutions, is that it doesn't handle 'draw states', but this could still be achieved with a slightly more involved reduce function.
a=['pear', 'apple', 'orange', 'apple'];
b={};
max='', maxi=0;
for(let k of a) {
if(b[k]) b[k]++; else b[k]=1;
if(maxi < b[k]) { max=k; maxi=b[k] }
}
As I'm using this function as a quiz for the interviewers, I post my solution:
const highest = arr => (arr || []).reduce( ( acc, el ) => {
acc.k[el] = acc.k[el] ? acc.k[el] + 1 : 1
acc.max = acc.max ? acc.max < acc.k[el] ? el : acc.max : el
return acc
}, { k:{} }).max
const test = [0,1,2,3,4,2,3,1,0,3,2,2,2,3,3,2]
console.log(highest(test))
Trying out a declarative approach here. This solution builds an object to tally up the occurrences of each word. Then filters the object down to an array by comparing the total occurrences of each word to the highest value found in the object.
const arr = ['hello', 'world', 'hello', 'again'];
const tally = (acc, x) => {
if (! acc[x]) {
acc[x] = 1;
return acc;
}
acc[x] += 1;
return acc;
};
const totals = arr.reduce(tally, {});
const keys = Object.keys(totals);
const values = keys.map(x => totals[x]);
const results = keys.filter(x => totals[x] === Math.max(...values));
This solution has O(n) complexity:
function findhighestOccurenceAndNum(a) {
let obj = {};
let maxNum, maxVal;
for (let v of a) {
obj[v] = ++obj[v] || 1;
if (maxVal === undefined || obj[v] > maxVal) {
maxNum = v;
maxVal = obj[v];
}
}
console.log(maxNum + ' has max value = ' + maxVal);
}
findhighestOccurenceAndNum(['pear', 'apple', 'orange', 'apple']);
For the sake of really easy to read, maintainable code I share this:
function getMaxOcurrences(arr = []) {
let item = arr[0];
let ocurrencesMap = {};
for (let i in arr) {
const current = arr[i];
if (ocurrencesMap[current]) ocurrencesMap[current]++;
else ocurrencesMap[current] = 1;
if (ocurrencesMap[item] < ocurrencesMap[current]) item = current;
}
return {
item: item,
ocurrences: ocurrencesMap[item]
};
}
Hope it helps someone ;)!
Here’s the modern version using built-in maps (so it works on more than things that can be converted to unique strings):
'use strict';
const histogram = iterable => {
const result = new Map();
for (const x of iterable) {
result.set(x, (result.get(x) || 0) + 1);
}
return result;
};
const mostCommon = iterable => {
let maxCount = 0;
let maxKey;
for (const [key, count] of histogram(iterable)) {
if (count > maxCount) {
maxCount = count;
maxKey = key;
}
}
return maxKey;
};
console.log(mostCommon(['pear', 'apple', 'orange', 'apple']));
Time for another solution:
function getMaxOccurrence(arr) {
var o = {}, maxCount = 0, maxValue, m;
for (var i=0, iLen=arr.length; i<iLen; i++) {
m = arr[i];
if (!o.hasOwnProperty(m)) {
o[m] = 0;
}
++o[m];
if (o[m] > maxCount) {
maxCount = o[m];
maxValue = m;
}
}
return maxValue;
}
If brevity matters (it doesn't), then:
function getMaxOccurrence(a) {
var o = {}, mC = 0, mV, m;
for (var i=0, iL=a.length; i<iL; i++) {
m = a[i];
o.hasOwnProperty(m)? ++o[m] : o[m] = 1;
if (o[m] > mC) mC = o[m], mV = m;
}
return mV;
}
If non–existent members are to be avoided (e.g. sparse array), an additional hasOwnProperty test is required:
function getMaxOccurrence(a) {
var o = {}, mC = 0, mV, m;
for (var i=0, iL=a.length; i<iL; i++) {
if (a.hasOwnProperty(i)) {
m = a[i];
o.hasOwnProperty(m)? ++o[m] : o[m] = 1;
if (o[m] > mC) mC = o[m], mV = m;
}
}
return mV;
}
getMaxOccurrence([,,,,,1,1]); // 1
Other answers here will return undefined.
Here is another ES6 way of doing it with O(n) complexity
const result = Object.entries(
['pear', 'apple', 'orange', 'apple'].reduce((previous, current) => {
if (previous[current] === undefined) previous[current] = 1;
else previous[current]++;
return previous;
}, {})).reduce((previous, current) => (current[1] >= previous[1] ? current : previous))[0];
console.log("Max value : " + result);
function mode(arr){
return arr.reduce(function(counts,key){
var curCount = (counts[key+''] || 0) + 1;
counts[key+''] = curCount;
if (curCount > counts.max) { counts.max = curCount; counts.mode = key; }
return counts;
}, {max:0, mode: null}).mode
}
Another JS solution from: https://www.w3resource.com/javascript-exercises/javascript-array-exercise-8.php
Can try this too:
let arr =['pear', 'apple', 'orange', 'apple'];
function findMostFrequent(arr) {
let mf = 1;
let m = 0;
let item;
for (let i = 0; i < arr.length; i++) {
for (let j = i; j < arr.length; j++) {
if (arr[i] == arr[j]) {
m++;
if (m > mf) {
mf = m;
item = arr[i];
}
}
}
m = 0;
}
return item;
}
findMostFrequent(arr); // apple
This solution can return multiple elements of an array in case of a tie. For example, an array
arr = [ 3, 4, 3, 6, 4, ];
has two mode values: 3 and 6.
Here is the solution.
function find_mode(arr) {
var max = 0;
var maxarr = [];
var counter = [];
var maxarr = [];
arr.forEach(function(){
counter.push(0);
});
for(var i = 0;i<arr.length;i++){
for(var j=0;j<arr.length;j++){
if(arr[i]==arr[j])counter[i]++;
}
}
max=this.arrayMax(counter);
for(var i = 0;i<arr.length;i++){
if(counter[i]==max)maxarr.push(arr[i]);
}
var unique = maxarr.filter( this.onlyUnique );
return unique;
};
function arrayMax(arr) {
var len = arr.length, max = -Infinity;
while (len--) {
if (arr[len] > max) {
max = arr[len];
}
}
return max;
};
function onlyUnique(value, index, self) {
return self.indexOf(value) === index;
}
const frequence = (array) =>
array.reduce(
(acc, item) =>
array.filter((v) => v === acc).length >=
array.filter((v) => v === item).length
? acc
: item,
null
);
frequence([1, 1, 2])
var array = [1, 3, 6, 6, 6, 6, 7, 7, 12, 12, 17],
c = {}, // counters
s = []; // sortable array
for (var i=0; i<array.length; i++) {
c[array[i]] = c[array[i]] || 0; // initialize
c[array[i]]++;
} // count occurrences
for (var key in c) {
s.push([key, c[key]])
} // build sortable array from counters
s.sort(function(a, b) {return b[1]-a[1];});
var firstMode = s[0][0];
console.log(firstMode);
Here is my solution to this problem but with numbers and using the new 'Set' feature. Its not very performant but i definitely had a lot of fun writing this and it does support multiple maximum values.
const mode = (arr) => [...new Set(arr)]
.map((value) => [value, arr.filter((v) => v === value).length])
.sort((a,b) => a[1]-b[1])
.reverse()
.filter((value, i, a) => a.indexOf(value) === i)
.filter((v, i, a) => v[1] === a[0][1])
.map((v) => v[0])
mode([1,2,3,3]) // [3]
mode([1,1,1,1,2,2,2,2,3,3,3]) // [1,2]
By the way do not use this for production this is just an illustration of how you can solve it with ES6 and Array functions only.
const mode = (str) => {
return str
.split(' ')
.reduce((data, key) => {
let counter = data.map[key] + 1 || 1
data.map[key] = counter
if (counter > data.counter) {
data.counter = counter
data.mode = key
}
return data
}, {
counter: 0,
mode: null,
map: {}
})
.mode
}
console.log(mode('the t-rex is the greatest of them all'))
Here is my solution :-
function frequent(number){
var count = 0;
var sortedNumber = number.sort();
var start = number[0], item;
for(var i = 0 ; i < sortedNumber.length; i++){
if(start === sortedNumber[i] || sortedNumber[i] === sortedNumber[i+1]){
item = sortedNumber[i]
}
}
return item
}
console.log( frequent(['pear', 'apple', 'orange', 'apple']))
Try it too, this does not take in account browser version.
function mode(arr){
var a = [],b = 0,occurrence;
for(var i = 0; i < arr.length;i++){
if(a[arr[i]] != undefined){
a[arr[i]]++;
}else{
a[arr[i]] = 1;
}
}
for(var key in a){
if(a[key] > b){
b = a[key];
occurrence = key;
}
}
return occurrence;
}
alert(mode(['segunda','terça','terca','segunda','terça','segunda']));
Please note that this function returns latest occurence in the array
when 2 or more entries appear same number of times!
With ES6, you can chain the method like this:
function findMostFrequent(arr) {
return arr
.reduce((acc, cur, ind, arr) => {
if (arr.indexOf(cur) === ind) {
return [...acc, [cur, 1]];
} else {
acc[acc.indexOf(acc.find(e => e[0] === cur))] = [
cur,
acc[acc.indexOf(acc.find(e => e[0] === cur))][1] + 1
];
return acc;
}
}, [])
.sort((a, b) => b[1] - a[1])
.filter((cur, ind, arr) => cur[1] === arr[0][1])
.map(cur => cur[0]);
}
console.log(findMostFrequent(['pear', 'apple', 'orange', 'apple']));
console.log(findMostFrequent(['pear', 'apple', 'orange', 'apple', 'pear']));
If two elements have the same occurrence, it will return both of them. And it works with any type of element.
// O(n)
var arr = [1, 2, 3, 2, 3, 3, 5, 6];
var duplicates = {};
max = '';
maxi = 0;
arr.forEach((el) => {
duplicates[el] = duplicates[el] + 1 || 1;
if (maxi < duplicates[el]) {
max = el;
maxi = duplicates[el];
}
});
console.log(max);
I came up with a shorter solution, but it's using lodash. Works with any data, not just strings. For objects can be used:
const mostFrequent = _.maxBy(Object.values(_.groupBy(inputArr, el => el.someUniqueProp)), arr => arr.length)[0];
This is for strings:
const mostFrequent = _.maxBy(Object.values(_.groupBy(inputArr, el => el)), arr => arr.length)[0];
Just grouping data under a certain criteria, then finding the largest group.
Here is my way to do it so just using .filter.
var arr = ['pear', 'apple', 'orange', 'apple'];
function dup(arrr) {
let max = { item: 0, count: 0 };
for (let i = 0; i < arrr.length; i++) {
let arrOccurences = arrr.filter(item => { return item === arrr[i] }).length;
if (arrOccurences > max.count) {
max = { item: arrr[i], count: arrr.filter(item => { return item === arrr[i] }).length };
}
}
return max.item;
}
console.log(dup(arr));
Easy solution !
function mostFrequentElement(arr) {
let res = [];
for (let x of arr) {
let count = 0;
for (let i of arr) {
if (i == x) {
count++;
}
}
res.push(count);
}
return arr[res.indexOf(Math.max(...res))];
}
array = [13 , 2 , 1 , 2 , 10 , 2 , 1 , 1 , 2 , 2];
let frequentElement = mostFrequentElement(array);
console.log(`The frequent element in ${array} is ${frequentElement}`);
Loop on all element and collect the Count of each element in the array that is the idea of the solution
Here is my solution :-
const arr = [
2, 1, 10, 7, 10, 3, 10, 8, 7, 3, 10, 5, 4, 6, 7, 9, 2, 2, 2, 6, 3, 7, 6, 9, 8,
9, 10, 8, 8, 8, 4, 1, 9, 3, 4, 5, 8, 1, 9, 3, 2, 8, 1, 9, 6, 3, 9, 2, 3, 5, 3,
2, 7, 2, 5, 4, 5, 5, 8, 4, 6, 3, 9, 2, 3, 3, 10, 3, 3, 1, 4, 5, 4, 1, 5, 9, 6,
2, 3, 10, 9, 4, 3, 4, 5, 7, 2, 7, 2, 9, 8, 1, 8, 3, 3, 3, 3, 1, 1, 3,
];
function max(arr) {
let newObj = {};
arr.forEach((d, i) => {
if (newObj[d] != undefined) {
++newObj[d];
} else {
newObj[d] = 0;
}
});
let nwres = {};
for (let maxItem in newObj) {
if (newObj[maxItem] == Math.max(...Object.values(newObj))) {
nwres[maxItem] = newObj[maxItem];
}
}
return nwres;
}
console.log(max(arr));
I guess you have two approaches. Both of which have advantages.
Sort then Count or Loop through and use a hash table to do the counting for you.
The hashtable is nice because once you are done processing you also have all the distinct elements. If you had millions of items though, the hash table could end up using a lot of memory if the duplication rate is low. The sort, then count approach would have a much more controllable memory footprint.
var mode = 0;
var c = 0;
var num = new Array();
var value = 0;
var greatest = 0;
var ct = 0;
Note: ct is the length of the array.
function getMode()
{
for (var i = 0; i < ct; i++)
{
value = num[i];
if (i != ct)
{
while (value == num[i + 1])
{
c = c + 1;
i = i + 1;
}
}
if (c > greatest)
{
greatest = c;
mode = value;
}
c = 0;
}
}
You can try this:
// using splice()
// get the element with the highest occurence in an array
function mc(a) {
var us = [], l;
// find all the unique elements in the array
a.forEach(function (v) {
if (us.indexOf(v) === -1) {
us.push(v);
}
});
l = us.length;
while (true) {
for (var i = 0; i < l; i ++) {
if (a.indexOf(us[i]) === -1) {
continue;
} else if (a.indexOf(us[i]) != -1 && a.length > 1) {
// just delete it once at a time
a.splice(a.indexOf(us[i]), 1);
} else {
// default to last one
return a[0];
}
}
}
}
// using string.match method
function su(a) {
var s = a.join(),
uelms = [],
r = {},
l,
i,
m;
a.forEach(function (v) {
if (uelms.indexOf(v) === -1) {
uelms.push(v);
}
});
l = uelms.length;
// use match to calculate occurance times
for (i = 0; i < l; i ++) {
r[uelms[i]] = s.match(new RegExp(uelms[i], 'g')).length;
}
m = uelms[0];
for (var p in r) {
if (r[p] > r[m]) {
m = p;
} else {
continue;
}
}
return m;
}

How can i get key of nested object = used For....in?

Here is my code. How can i get the key of the key-value pair using for loop?
var apartment = {
bedroom: {
area: 20,
bed: {
type: 'twin-bed',
price: 100
}
}
};
The desired output is as follows:
/* desired results :
* bedroom
* area
* bed
* type
* price
*/
Please help
var getKeys = function(obj) {
var keys = [];
Object.keys(obj).forEach(function(key){
keys.push(key);
if(typeof obj[key] == 'object'){
keys = keys.concat(getKeys(obj[key]));
}
})
return keys;
}
Then
var keys = getKeys(apartment);
You can use a simple Regex as follow:
var apartment = {
bedroom: {
area: 20,
bed: {
type: 'twin-bed',
price: 100
}
}
};
let result = [];
let jsonstr = JSON.stringify(apartment);
// {"bedroom":{"area":20,"bed":{"type":"twin-bed","price":100}}}
let regex = /"(\w+)":/g;
jsonstr.replace(regex, function(match,prop){
result.push(prop);
});
console.log(result);
we can easily done by using regex, convert object string and apply regex to extract the particular word
run the snippet for required output
var apartment = {
bedroom: {
area: 20,
bed: {
type: 'twin-bed',
price: 100
}
}
};
apartment = JSON.stringify(apartment);
var re = /(")\w+(")(:)/g;
var match;
do {
match = re.exec(apartment);
if (match) {
console.log(match[0]);
}
} while (match);
regex : /(")\w+(")(:)/g
only extracts key for more click here
do while loop responsible to detect multiple match in the string
You can use a recursive function :
function getKeys(source, dest) {
for (let key in source) {
if (typeof source[key] == 'object') {
dest.push(key)
getKeys(source[key], dest)
} else {
dest.push(key)
}
}
return dest
}
result = []
const apartment = {
bedroom: {
area: 20,
bed: {
type: 'twin-bed',
price: 100
}
}
}
getKeys(apartment, result) // ["bedroom", "area", "bed", "type", "price"]
var inputs = [
{a:1,b:2,c:3}, // Simple object
{a:{b:2,c:3}}, // Simple object with nesting
{a:{a:{b:2,c:3}}}, // Repeated key hiding nesting
{a:[{b:2,c:3}]}, // keys behind array
];
inputs.push(inputs); // reference cycle and array at top
function getKeys(obj) {
var all = {};
var seen = [];
checkValue(obj);
return Object.keys(all);
function checkValue(value) {
if (Array.isArray(value)) return checkArray(value);
if (value instanceof Object) return checkObject(value);
}
function checkArray(array) {
if (seen.indexOf(array) >= 0) return;
seen.push(array);
for (var i = 0, l = array.length; i < l; i++) {
checkValue(array[i]);
}
}
function checkObject(obj) {
if (seen.indexOf(obj) >= 0) return;
seen.push(obj);
var keys = Object.keys(obj);
for (var i = 0, l = keys.length; i < l; i++) {
var key = keys[i];
all[key] = true;
checkValue(obj[key]);
}
}
}
var result = inputs.map(getKeys);
console.log(result);

How do I write a function that takes an array of values and returns an object

in this format; sample input: ["book", 1, "table", 4] sample output: { string: ["book", "table"], number: [1,4] }
here is the code i have written but it's not giving me the output i want.
function listDic(arr) {
if (Array.isArray(arr)) {
let output = {};
for (let i =0; i == arr.length; i++) {
if (typeof arr[i] === 'string') {
var str = [];
str.push(arr[i]);
}
if (typeof arr[i] === 'number') {
var num = [];
num.push(arr[i]);
}
}
return {string: str, number: num}
}
return "Only arrays are allowed.";
}
please what exactly am i getting wrong?
You could use the type directly as key for the object, while iterating the array.
If you have not an object with the key, generate a new one with an empty array for pushing the actual item.
var array = ["book", 1, "table", 4],
object = {};
array.forEach(function (a) {
var type = typeof a;
object[type] = object[type] || [];
object[type].push(a);
});
console.log(object);
You're overwriting str and num in the loop without ever keeping any other reference to the arrays. Put those arrays in the output object, and then push into those.
var data = ["book", 1, "table", 4];
console.log(listDic(data));
function listDic(arr) {
if (!Array.isArray(arr)) {
return "Only arrays are allowed.";
}
let output = {
string: [],
number: []
};
for (let i = 0; i < arr.length; i++) {
switch (typeof arr[i]) {
case "string":
output.string.push(arr[i]);
break;
case 'number':
output.number.push(arr[i]);
}
}
return output;
}
You could just reduce the array into an object, and use the type of the values as keys
var arr = ["book", 1, "table", 4];
function listDic(arr) {
return arr.reduce((a, b) => {
let t = typeof b;
a[t] ? a[t].push(b) : a[t] = [b];
return a;
}, {});
}
console.log(listDic(arr));
thanks i got a better way to solve it myself
function listDic(arr) {
if (Array.isArray(arr)) {
let str = [];
let num = [];
for (let i =0; i < arr.length; i++) {
if (typeof arr[i] === 'string') {
str.push(arr[i]);
}
if (typeof arr[i] === 'number') {
num.push(arr[i]);
}
}
return {string: str, number: num}
}
return "Only arrays are allowed.";
}
I think this should work for you :
function listDic(arr) {
if (Array.isArray(arr)) {
let output = {};
var str = [],
num = []; //CORRECTED
for (let i = 0; i < arr.length; i++) { //CORRECTED
if (typeof arr[i] === 'string') {
str.push(arr[i]);
}
if (typeof arr[i] === 'number') {
num.push(arr[i]);
}
}
return { string: str, number: num }
}
return "Only arrays are allowed.";
}
console.log(listDic(["book", 1, "table", 4]));
Explanation
Basically, there were two issues:
you were really not entering the loop with == arr.length on your for-loop's entry condition.
you kept re-assigning the str and num arrays with a re-declaration var str[i] = [] OR var num[i] = [].
Hope this helps.

Add or remove element(s) to array

I have an existing array of objects :
existingArray = [
{object1: 'object1'},
{object2: 'object2'}
{object3: 'object3'},
]
I receive a new one :
newArray = [
{object2: 'object2'},
{object3: 'object3'},
{object4: 'object4'}
]
I want only to modify the existing one to get the new one as the result (push+splice)
Here is what I have for now (is there a better way ?)
for (var i = 0; i < newArray.length; i++) {
// loop first to push new elements
var responseToTxt = JSON.stringify(newArray[i]);
var newStatement = false;
for(var j = 0; j < existingArray.length; j++){
var statementToTxt = JSON.stringify(existingArray[j]);
if(statementToTxt === responseToTxt && !newStatement){
newStatement = true;
}
}
if(!newStatement){
statements.push(response[i]);
}
}
var statementsToSplice = [];
for (var i = 0; i < existingArray.length; i++) {
// then loop a second time to split elements not anymore on the new array
var statementToTxt = JSON.stringify(existingArray[i]);
var elementPresent = false;
var element = false;
for(var j = 0; j < newArray.length; j++){
var responseToTxt = JSON.stringify(newArray[j]);
if(responseToTxt === statementToTxt && !elementPresent){
elementPresent = true;
} else {
element = i;
}
}
if(!elementPresent){
statementsToSplice.push(element);
}
}
Then I needed to split multiple times in the array :
existingArray = statementsToSplice.reduceRight(function (arr, it) {
arr.splice(it, 1);
return arr;
}, existingArray.sort(function (a, b) { return b - a }));
Here is the example :
https://jsfiddle.net/docmz22b/
So the final output should always be the new array, but only by push or splice the old one.
In this case, the final outpout will be
existingArray = [
{object2: 'object2'},
{object3: 'object3'}
{object4: 'object4'},
]
The new array could contains multiple new elements and/or deleted elements that is currently in the existingArray
Use shift() and push()
existingArray.shift(); //Removes the first element of the array
existingArray.push({'object4' : 'object4'});
Fiddle
I'm almost 100% sure that there is a better way to do it, but at least this works, feel free to comment any suggestions / optimizations.
existingArray = [
{object1: 'object1'},
{object2: 'object2'},
{object3: 'object3'}
];
newArray = [
{object2: 'object2'},
{object3: 'object3'},
{object4: 'object4'}
];
// Loop all the old values, if is not in the new array, remove it
existingArray.forEach(function(item) {
if(!inArray(item, newArray)) {
var idx = indexOfObjectInArray(item, existingArray);
existingArray.splice(idx, 1);
}
});
// Loop all the new values, if is not in the new array, push it
newArray.forEach(function(item) {
if (!inArray(item, existingArray)) {
existingArray.push(item);
}
});
// Auxiliar functions
function inArray(initialValue, array) {
testValue = JSON.stringify(initialValue);
return array.some(function(item) {
return testValue == JSON.stringify(item);
});
}
function indexOfObjectInArray(initialValue, array) {
var result = -1;
testValue = JSON.stringify(initialValue);
array.forEach(function(item, idx) {
if (testValue == JSON.stringify(item)) {
result = idx;
};
});
return result;
}
Maybe this helps. It features Array.prototype.forEach and Array.prototype.some.
Splice unwanted items
Look if object with same property exist
If yes, then assign new object
Else push the object
var existingArray = [
{ object1: 'object1' },
{ object2: 'object2' },
{ object3: 'object3' },
],
newArray = [
{ object2: 'object22' },
{ object3: 'object33' },
{ object4: 'object44' }
];
function update(base, change) {
var changeKeys = change.map(function (a) { return Object.keys(a)[0]; }),
i = 0;
while (i < base.length) {
if (!~changeKeys.indexOf(Object.keys(base[i])[0])) {
base.splice(i, 1);
continue;
}
i++;
}
change.forEach(function (a) {
var aKey = Object.keys(a)[0];
!base.some(function (b, i, bb) {
if (aKey === Object.keys(b)[0]) {
bb[i] = a; // if that does not work, use bb.splice(i, 1, a);
return true;
}
}) && base.push(a);
});
}
update(existingArray, newArray);
document.write('<pre>' + JSON.stringify(existingArray, 0, 4) + '</pre>');

Access nested objects via array of property names

Say I have an object like this (simplified):
var options = {
boxes: {
size: {
x: 15,
y: 18
},
shadow: {
[...]
}
};
And I have an array of names:
var names = ['boxes', 'size', 'x'];
What is an easy way to get/set a value inside the object according to the array, in this example it would be:
options.boxes.size.x = somevalue;
Any ideas?
There's no easy, built-in method for doing this. You'd have to write your own method:
function getPath(obj, props) {
for(var i = 0; i < props.length; i++) {
if (props[i] in obj) {
obj = obj[props[i]];
} else {
return; // not found
}
}
return obj;
}
function setPath(obj, value, props) {
for(var i = 0; i < props.length - 1; i++) {
if (props[i] in obj) {
obj = obj[props[i]];
} else {
return; // not found
}
}
obj[props[i]] = value;
}
alert(getPath(options, names)); // 15
setPath(options, 25, names);
alert(getPath(options, names)); // 25
Just use a loop that iterates the names and grabs the next nested object for the current name. Either a falsey value or the end of the array should halt the loop.
var obj = options;
var i = 0;
while (obj && i < names.length)
obj = obj[names[i++]];
Or simply use .reduce()
names.reduce(function(obj, name) {
return obj && obj[name];
}, options);
And of course you can name and reuse the function if you prefer.
function toPropertyIn(obj, name) {
return obj && obj[name];
}
names.reduce(toPropertyIn, options);
To make a getter/setter:
function nestedProp(obj, names, value) {
if (arguments.length > 1)
var setProp = names.pop();
var res = names.reduce(function(obj, name) {
return obj && obj[name];
}, options);
if (res && setProp !== undefined)
res[setProp] = value;
else
return res;
}
nestedProp(options, names, "foo"); // to set
var val = nestedProp(options, names); // to get

Categories

Resources