Array to object function using JavaScript - javascript

For the function Objectgenerator(input1, input2), take arrays as input. Each array must have only two elements. The goal is to use JavaScript to create an object that will have the first elements of each array as the key and the second element as the value. In the event more than one array has similar first elements, then consider only the value of the last one. For example:
Examples:
Objectgenerator(["key1","value1"], ["key2","value2"],["key1","value3"]) would result in the following output --> {key1:"value3", key2:"value2"}
Objectgenerator(["Key1","value1"],["key2","value2"]) --> {key1:"value1",key2:"value2"}
Errors would result if the following parameters are passed:
Objectgenerator([])
Objectgenerator()
Objectgenerator('testing')
Objectgenerator(22)
Objectgenerator(['key1','valuea','valueb'],['key2','valuec'])

Basically, Array#reduce is your best bet. Setting it up is straight forward. The actual work is in verifying that the input is valid.
function objectGenerator(...arr) {
if( arr.length &&
arr.every(el => Array.isArray(el) && el.length === 2) ) {
return arr.reduce((obj, [key,value]) => ({...obj,[key]:value}),{});
} else {
const err = new Error('Input is not valid');
throw err;
}
}
console.log( objectGenerator(["key1","value1"], ["key2","value2"],["key1","value3"]) );
console.log( objectGenerator(["Key1","value1"],["key2","value2"]) );

Related

How can create a function like findSum([1,2,3,5], 5) that second param is the sum of to element of array and return those two element in javascript?

I wanna write a function that receive two parameter that first one is an array and the second is an integer; So I wanna return two element of the array that their sum be equal the second function's parameter. For example in this case findSum([1,2,3,5], 5) my function will return 2 and 3 that their sum being 5. I wrote a function but I think it can be better with another optimized coding.
function findSum(arr, sum){
for(element of arr) {
const first_element = element;
for(innerElement of arr){
if((innerElement !== first_element) && (innerElement + element === sum) )
return {first_element, innerElement}
}
}
}
So, one clear issue with your code is that findSum([2,2,3],4) won't work, because of this line: if((innerElement !== first_element). You should check indexes, rather than values to avoid this.
Very quick example:
findSum = (arr, sum) => {
return arr.map((x, i) => {
return arr.map((y, j) => {
if(i === j) return null;
return x + y === sum ? {x, y} : null;
}).filter(x => x);
}).flat()[0];
}
Essentially, what I''m doing is very similar to your original version, with some slight tweaks.
Rather than using two for loops like you, I use two maps to iterate over the array. This gives me access to the index (i & j).
If i === j then it's the exact same element (and not just the same value) so we can ignore it. This solves the if((innerElement !== first_element)
issue.
Then I check to see if the sum is correct and return the values, or null if it's incorrect. Filtering this array with filter(x => x) returns only the truthy elements, i.e. removes the nulls so we are left with all the matches.
We then flatten the array and return the first object as that's all we need, but we could change that to return all the matches. findSum([2,2,1,3], 4) has two matches for example.
Key concepts:
Array.map
Array.filter
Array.flat

Filter an array of objects using the filter method with multiple tests

I'd like to filter an array of objects based on multiple tests. For this example, I want to filter an array of objects if the values for the keys aren't null, and that one value for one key is less than 90. I'm currently doing this with a for loop like so:
let filtered = []
for (let i = 0; i < articles.length; i++) {
if (articles[i].title !== null && articles[i].title.length <= 90 &&
articles[i].date !== null &&
articles[i].image !== null &&
articles[i].description !== null &&
articles[i].link !== null) {
filtered.push(articles[i])
}
}
But it's pretty clumpy and I know the filter method can achieve something similar. But I'm unsure if it can check multiple keys and their values with the same test whilst checking if a specific value passes an independent test too.
Try:
articles.filter(article =>
Object.values(article).every(x => (x !== null))
&& article.title.length <= 90
)
Let's break this down:
articles.filter(article => ...)
.filter is a function property of type that accepts a callback argument, which it calls for each item. Essentially, we're passing it a function - not executing it right away, which it can call at its leisure. It's sort of like:
let a = alert;
We're not calling the alert function, we're just saving it to a variable. In the case of .filter, we're using it as a pseudo-variable - an argument. Internally, all .filter is doing is:
Array.prototype.filter(callbackFunc) {
newArr = [];
for (i=0;i<this.length;i++){
if (callbackFunc(this[i]) === false){ // We're calling `callbackFunc` manually, for each item in the loop.
newArr.push(this[i]);
}
}
return newArr;
}
The next bit to explain is the actual callback function we're using. It's defined with ES6 arrow syntax, but it's the equivalent of:
articles.filter(function(article){
return Object.values(article).every(x => (x !== null))
&& article.title.length <= 90
})
The first line of the callback function, Object.values(article).every(x => (x !== null)), can be broken down to:
let values = Object.values(article); // Get the value of all of the keys in the object
function checkFunction(item){ // Define a helper function
return (x !== null); // Which returns if an item is _not_ null.
}
let result = values.every(checkFunction); // Run `checkFunction` on every item in the array (see below), and ensure it matches _all_ of them.
Finally, we just need to clarify what every does. It's another example of functional JS, where functions accept callback functions as parameters. The internal code looks like this:
Array.prototype.every(callbackFunc) {
for (i=0;i<this.length;i++){
if (callbackFunc(this[i]) === false){ // We're calling `callbackFunc` manually, for each item in the loop.
return false;
}
}
// In JS, returning a value automatically stops execution of the function.
// So this line of code is only reached if `return false` is never met.
return true;
}
And && article.title.length <= 90 should hopefully be self-explanatory: while .every returns a boolean value (true or false), a true will only be returned by the callback function to the filter if the second condition is also met, i.e if the every returns true and article.title.length <= 90
The filter method does exactly this: it takes a conditional (just like that in your if statement and adds it to the array if the condition is met. Your code almost matches the filter syntax exactly, actually:
let filtered = articles.filter(article =>
article.title !== null
article.title.length <= 90 &&
article.date !== null &&
article.image !== null &&
article.description !== null &&
article.link !== null);
yes filter can do this, it just takes a function and applies it to each item in the array
array.filter(x => x.title != null && ... etc)
the examples in this section is pretty much what you are doing https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter#Filtering_invalid_entries_from_JSON

Return subset of JSON Object using Javascript map() function

My question is if there is a simple way to return a subset of JSON object that will contain all 'columns' rather than specifying individually which 'columns' to return.
In particular, I have a multi-column csv file converted to JSON. The function below returns a subset of that object - just two 'columns' (as another object) if certain condition is met (in this case 'Name' is matched):
var firstSubArr = json.map(function(s) {
if (s.Name === RegName) {
return {
'Price': s.Price,
'Name': s.Name
}
}
}).filter(function(n) {
return n !== undefined
});
Is there a simpler way to return "all columns" from json object as object rather that this:
return {'Price':s.Price,'Name':s.Name}?
Comment:
It is just a simple JSON structure after conversion from csv, eg:
`[
{Name:'Sydney', Price:123, Type:'xyz', etc. 20 more... },
etc.
]`
Comment 2: Yes filter may be an option as a couple of people suggested but what if my condition is a bit more complex, like:
var fullPeriodArr= json.map( function (s) {
if(moment(s.Date,"YYYYMMDD").isSameOrBefore(currd) && moment(s.Date,"YYYYMMDD").isSameOrAfter(fullPeriodStart) && (s.Type==sid || s.Type==sid2)&& s.Name===RegName){return [s.Price, s.Name]
}
}).filter( function (n) {
return n!== undefined
});
SOLUTION:
All 3 respondents provided the clue, thanks! It was just “return every object in the array of objects that matches the condition”, or simply:
var firstSubArr= json.filter(
s => (moment(s.Date,"YYYYMMDD").isSameOrBefore(currd) && moment(s.Date,"YYYYMMDD").isSameOrAfter(fullPeriodStart) && (s.Type==sid || s.Type==sid2) && s.Name===RegName)
);
. where time range 'from... to' is evaluated using moment.js lib and Date, Type and Name are object keys.
The secret pudding was the round bracket ( ) around the complex condition.
How neat Javascript has become: no need to get .length nor to loop with 'for' or 'while', no 'if... else' statements, or no need to store interim results, and no 'return'. The arrow replaces all that!
Then you can access eg. Price 'column' as array of numbers for summary calculations (eg. sum of values):
var firstSubArrPrice=firstSubArr.map(t => t.Price );
Get rid of the map() and just use filter() and you will have the original objects in resultant
var firstSubArr = json.filter(function(s) {
return s.Name === RegName
});
I understood that you want to return the the object with specific properties which matches the condition.You can do that using reduce()
var firstSubArr = json.reduce((ac,{Name,Price}) => a.Name === RegName ? [...ac,{Name,Price}] : ac, []);
If you don't want only Name,Price and all properties with calling names. Then use filter() alone
var firstSubArr = json.filter(x => x.Name === RegName)
How about:
var firstSubArr = json.filter( x => x.Name ===RegName)
To make it in JavaScript I recommend this code:
var valueNeedly = json.filter(function(s) {
return s.value === searchValue
});

replace object in array base on index

So, Im using react and I need to keep adding objects to an array of objects (object may have the same index, thats why I check for label and index). When the object that I want to add has the same label property as one that already is in that array, it should replace the previous object. So, lets say, only one object for each label. What I have works until I work with more then one label. When I do so, the array accepts more than one objects for each label...
if (this.state.thumbnailsAtivas.some(thumbnail => {
thumbnail.index === textura.index
}) && this.state.thumbnailsAtivas.some(thumbnail => {
thumbnail.label === textura.label
})) {
console.log("already in array");
}
else if (this.state.thumbnailsAtivas.some(thumbnail => thumbnail.label === textura.label)) {
console.log("label already with item");
this.state.thumbnailsAtivas.some((thumbnail, index) => {
const tempData = (this.state.thumbnailsAtivas).slice(0);
tempData[index] = textura;
this.setState({thumbnailsAtivas: tempData})
})
} else {
this.setState({thumbnailsAtivas: [...this.state.thumbnailsAtivas, textura]},);
}
You can use another Array function called findIndex which have the same usage as some but returns a result like indexOf does (returns the index of the element in an array or -1 if no element matches):
let index = this.state.thumbnailsAtivas.findIndex(
thumbnail => thumbnail.label === textura.label
);
if(index !== -1) {
this.state.thumbnailsAtivas[index] = yourNewObject;
}
Note: To optimise your code a little bit, you could get rid of the call to some and use findIndex (once) for both checking existence and finding the index.

How to check if values in one JavaScript object are present in another one?

I am trying to compare json_str1 and json_str2, here it should return true as all elements in json_str1 are present in json_str2.
For now I am doing this the long way like this
json_str1 = '{"0":"a","1":"b","2":"c"}';
json_str2 = '{"0":"c","1":"b","2":"a"}';
json_obj1 = $.parseJSON(json_str1);
json_obj2 = $.parseJSON(json_str2);
arr1 = $.map(json_obj1, function(el) { return el });
arr2 = $.map(json_obj2, function(el) { return el });
if($(arr1).not(arr2).length === 0 && $(arr2).not(arr1).length === 0)
alert("equal");
else
alert("not equal");
How could I make it short and simple, without converting the objects into an array ?
https://jsfiddle.net/kq9gtdr0/
Use the following code:
Object.keys(json_obj1) . every(k1 =>
Object.keys(json_obj2) . some(k2 =>
json_obj1[k1] === json_obj2[k2]
)
);
In English:
Every key k1 in json_obj1 satisfies the condition that some key k2 in json_obj2 satisifies the condition that the value of json_obj1 with key k1 is equal to the value of json_obj2 with key k2.
Or in more conversational English:
Every value in the first object matches some value in the second.
Using lodash
var _ = require('lodash');
function compareValues(jstr1, jstr2) {
return _.isEqual(_.valuesIn(JSON.parse(jstr1)).sort(), _.valuesIn(JSON.parse(jstr2)).sort());
}
json_str1 = '{"0":"a","1":"b","2":"c"}';
json_str2 = '{"0":"c","1":"b","2":"a"}';
console.log(compareValues(json_str1, json_str2));
There is short and easy accurate way to this.
You can use a third party but extremely popular utility library called Lodash. Chaining functions you can check for equality.
First parse both JSON into objects
Then use _.values() to extract the values of all keys of each into separate arrays
Find difference of two arrays. If its an empty array then both of them are equal.
You can chain all the steps into one statement like:
_.isEmpty(_.difference(_.values(json_obj1), _.values(json_obj2)))
Example: https://jsfiddle.net/kq9gtdr0/4/
For more information:
https://lodash.com/docs#values
https://lodash.com/docs#difference
https://lodash.com/docs#isEmpty
You can include the library from CDN(https://cdn.jsdelivr.net/lodash/4.5.1/lodash.min.js) or download and use it as normal script. Lodash offers plenty of useful utility functions that makes JS programming a lot easier. You better try it out.
If you prefer using libraries, then you could use underscore isMatch
_.isMatch(object, properties)
Tells you if the keys and values in properties are contained in
object.
Extending the awesome answer by #user663031, in case you need to do deep comparison, here's code that works:
export function objectOneInsideObjectTwo(jsonObj1: any, jsonObj2: any): boolean {
return Object.keys(jsonObj1).every((k1) => {
if (parseType(jsonObj1[k1]) === 'dict') {
return objectOneInsideObjectTwo(jsonObj1[k1], jsonObj2[k1]);
}
if (parseType(jsonObj1[k1]) === 'array') {
const results: boolean[] = [];
jsonObj1[k1].forEach((o: any, i: number) => {
if (parseType(o) === 'dict') {
results.push(objectOneInsideObjectTwo(o, jsonObj2[k1][i]));
} else {
results.push(o === jsonObj2[k1][i]);
}
});
return results.every((r) => r);
}
return Object.keys(jsonObj2).some((k2) => jsonObj1[k1] === jsonObj2[k2]);
});
}
export function parseType<T>(v: T): string {
if (v === null || v === undefined) {
return 'null';
}
if (typeof v === 'object') {
if (v instanceof Array) {
return 'array';
}
if (v instanceof Date) {
return 'date';
}
return 'dict';
}
return typeof v;
}
You can try this
var json_str1 = {"0":"a","1":"b","2":"c"};
var json_str2 = {"0":"c","1":"b","2":"a"};
var flag = 1;
if(Object.keys(json_str1).length == Object.keys(json_str2).length){
Object.keys(json_str1).forEach(function(x){
if(!json_str2.hasOwnProperty(x) || json_str2[x] != json_str1[x]){
flag = 0;
return;
}
});
}
if(flag)
alert('equal');
else
alert('Not Equal');
If you want to find out if both Objects have the same keys there is no way to do this without at least converting the keys of both Objects to an array with Object.keys or looping through both Objects!
The reason is simple: It's clear that you have to compare the number of keys of both Objects and the only way to do this is by looping through all properties or Object.keys.
So I think the shortest way to do this is:
json_obj1 = JSON.parse('{"0":"a","1":"b","2":"c"}');
json_obj2 = JSON.parse('{"0":"c","1":"b","2":"a"}');
keys_1 = Object.keys(json_obj1);
keys_2 = Object.keys(json_obj2);
if(keys_1.length === keys_2.length && keys_1.every(key => keys_2.indexOf(key) >= 0)) {
alert('equal')
} else {
alert('not equal')
}
If you only want to check if all keys from json1 are present in json2 you can do:
json_obj1 = JSON.parse('{"0":"a","1":"b","2":"c"}');
json_obj2 = JSON.parse('{"0":"c","1":"b","2":"a"}');
if(Object.keys(json_obj1).every(key => key in json_obj2)) {
alert('equal');
} else {
alert('not equal');
}
In your question and comments you indicate you are only looking to verify that "all elements in json_str1 are present in json_str2". Your example code doesn't just do that, it checks for the complete equality of keys by testing if all the keys (not values) in the first object are in the second object AND all the keys in the second object are in the first object. By looking at your code, i assume that when you say "elements" you mean keys.
All that aside, this might help:
// Your first few lines of code
json_str1 = '{"0":"a","1":"b","2":"c"}';
json_str2 = '{"0":"c","1":"b","2":"a"}';
json_obj1 = $.parseJSON(json_str1);
json_obj2 = $.parseJSON(json_str2);
// My new code
var values1 = Object.keys(json_obj1).map(key => json_obj1[key]);
var values2 = Object.keys(json_obj2).map(key => json_obj2[key]);
// Check if every key in the first object is in the second object.
values1.every(k1 => values2.indexOf(k1) >= 0);
// OR
// Check for equality of keys by checking both directions.
values1.every(k1 => values2.indexOf(k1) >= 0) && values2.every(k2 => values1.indexOf(k2) >= 0);
That's 2 lines to get the keys, and one line to check. You only need one of those two checks.

Categories

Resources