Can I assign 2 let in for loop? I tried this but gives me warning k[ts] ')' expected.:
for(let key in app.date let key2 in app.date2) {
data_series.push({
"year": app.date[key]._id,
"italy": app.date[key].count,
"germany": app.date2[key2].count2,
});
}
What is the correct syntax?
Assuming that both objects have the same number of keys, in order to iterate over both at once, you'd have to first construct an object of some sort that contains key pairs. But, from your code, it looks like you're interested only in the value at that key, not the key itself - if what you want is clear, short code, you might use an array method instead: iterate over the Object.values of app.date, and use the index to access the appropriate value inside date2:
const date2Values = Object.values(app.date2);
Object.values(app.date).forEach(({ _id, count }, i) => {
data_series.push({
year: _id,
italy: count,
germany: date2Values[i].count2
});
});
If you had to use for, then construct an object of key pairs beforehand, and iterate with for..of:
const date2Keys = Object.keys(app.date2);
const allKeys = Object.keys(app.date)
.map((key, i) => ({ key, key2: date2Keys[i] }));
for (const { key, key2 } of allKeys) {
// etc
}
or, it might be clearer with a plain for loop instead:
const dateKeys = Object.keys(app.date);
const date2Keys = Object.keys(app.date2);
for (let i = 0, { length } + Object.keys(app.date); i < length; i++) {
const key = dateKeys[i];
const key2 = date2Keys[i];
// etc
}
But the Object.values version is probably preferable, since it's more abstract, functional, and doesn't require manual iteration.
Also, if data_series is an empty array beforehand, it would be more appropriate to use .map than to use forEach and push:
const date2Values = Object.values(app.date2);
const data_series = Object.values(app.date).map(({ _id, count }, i) => ({
year: _id,
italy: count,
germany: date2Values[i].count2
});
If at all possible, I'd recommend changing your data structure so that each item of date and date2 is clearly associated with the other, such as an array, rather than depending on each object happening to have the same ordered property names. Although property order is guaranteed in modern browsers, it's not a good thing for code to depend on.
Related
How do I search an array for any instances of multiple specified string values?
const arrayOfObjects = [{
name: box1,
storage: ['car', 'goat', 'tea']
},
{
name: box2,
storage: ['camel', 'fox', 'tea']
}
];
arrayOfSearchItems = ['goat', 'car', 'oranges'];
If any one or all of the arrayOfSearchItems is present in one of the objects in my array, I want it to either return false or some other way that I can use to excluded that object that is in my arrayOfObjects from a new, filtered arrayOfObjects without any objects that contained the arrayOfSearchItems string values. In this case I would want an array of objects without box1.
Here is what I have tried to do, based on other suggestions. I spent a long time on this. The problem with this function is that it only works on the first arrayOfSearchItems strings, to exclude that object. It will ignore the second or third strings, and not exclude the object, even if it contains those strings. For example, it will exclude an object with 'goat'. Once that happens though, it will no longer exclude based on 'car'. I have tried to adapt my longer code for the purposes of this question, I may have some typos.
const excludeItems = (arrayOfSearchItems, arrayOfObjects) => {
let incrementArray = [];
let userEffects = arrayOfSearchItems;
let objects = arrayOfObjects;
for (i = 0; i < userEffects.length; i++) {
for (x = 0; x < objects.length; x++) {
if (objects[x].storage.indexOf(userEffects) <= -1) {
incrementArray.push(objects[x]);
}
}
}
return(incrementArray);
}
let filteredArray = excludeItems(arrayOfSearchItems, arrayOfObjects);
console.log(filteredArray);
Thanks for providing some example code. That helps.
Let's start with your function, which has a good signature:
const excludeItems = (arrayOfSearchItems, arrayOfObjects) => { ... }
If we describe what this function should do, we would say "it returns a new array of objects which do not contain any of the search items." This gives us a clue about how we should write our code.
Since we will be returning a filtered array of objects, we can start by using the filter method:
return arrayOfObjects.filter(obj => ...)
For each object, we want to make sure that its storage does not contain any of the search items. Another way to word this is "every item in the starage array does NOT appear in the list of search items". Now let's write that code using the every method:
.filter(obj => {
// ensure "every" storage item matches a condition
return obj.storage.every(storageItem => {
// the "condition" is that it is NOT in the array search items
return arrayOfSearchItems.includes(storageItem) === false);
});
});
Putting it all together:
const excludeItems = (arrayOfSearchItems, arrayOfObjects) => {
return arrayOfObjects.filter(obj => {
return obj.storage.every(storageItem => {
return arrayOfSearchItems.includes(storageItem) === false;
});
});
}
Here's a fiddle: https://jsfiddle.net/3p95xzwe/
You can achieve your goal by using some of the built-in Array prototype functions, like filter, some and includes.
const excludeItems = (search, objs) =>
objs.filter(({storage:o}) => !search.some(s => o.includes(s)));
In other words: Filter my array objs, on the property storage to keep only those that they dont include any of the strings in search.
Suppose I have an array of object:
const apple = [{"bookName" :'Harry Pottar',part:"1"},{"bookName" :'Harry Pottar',part:"2"},
{"bookName": 'LOTR',part:"1"},{"bookName": 'LOTR',part:"2"},{"bookName": 'LOTR',part:"3"}]
I want to get count of all common values along with the value name as :
Expected O/P : [{"Harry Pottar":2},{"LOTR":3"}]
For this I tried as:
const id = "Harry Pottar";
const count = array.reduce((acc, cur) => cur.bookName === id ? ++acc : acc, 0);
As this gives the count, by this I can get count for each bookName. But how can I achieve my expected O/P scenario.
If anyone needs any further information please do let me know.
Good to see you know about .reduce! You’re pretty close, just need to save the result to a hashmap (plain object in JS).
const array = [{"bookName" :'Harry Pottar',part:"1"},{"bookName" :'Harry Pottar',part:"2"},{"bookName": 'LOTR',part:"1"},{"bookName": 'LOTR',part:"2"},{"bookName": 'LOTR',part:"3"}]
const result = array.reduce((acc, item) => {
const key = item.bookName
if (!acc.hasOwnProperty(key)) {
acc[key] = 0
}
acc[key] += 1
return acc
}, {})
// not sure why you want the result to be multiple objects. But here you go:
const output = Object.entries(result).map(([key, value])=> ({ [key]: value }))
Create a map from your data keyed by the book names, where the corresponding values are the objects you want in the output, with the count set to zero (you can use the computed property name syntax for the object's dynamic property). Then iterate the data again to increment the counters. Finally extract the values from the map into an array:
const apple = [{"bookName" :'Harry Pottar',part:"1"},{"bookName" :'Harry Pottar',part:"2"},
{"bookName": 'LOTR',part:"1"},{"bookName": 'LOTR',part:"2"},{"bookName": 'LOTR',part:"3"}];
let map = new Map(apple.map(({bookName}) => [bookName, { [bookName]: 0 }]));
for (let {bookName} of apple) map.get(bookName)[bookName]++;
let result = Array.from(map.values());
console.log(result);
You were pretty close. You don't necessarily need to have those objects in an array though. Just have an object with the booknames as the property keys. It would make it easier to manage.
If you then want to create an array of objects from that data you can use map over the Object.entries of that object.
const apple = [{"bookName" :'Harry Pottar',part:"1"},{"bookName" :'Harry Pottar',part:"2"},{"bookName": 'LOTR',part:"1"},{"bookName": 'LOTR',part:"2"},{"bookName": 'LOTR',part:"3"}];
const out = apple.reduce((acc, { bookName }) => {
// If the property doesn't exist, create it
// and set it to zero, otherwise increment the value
// of the existing property
acc[bookName] = (acc[bookName] || 0) + 1;
return acc;
}, {});
console.log(out);
const result = Object.entries(out).map(([ key, value ]) => {
return { [key]: value };
});
console.log(result);
so I want to find unique values from an array.
so for example I have this array:
const mainArr = ['shape-10983', 'size-2364', 'size-7800', 'size-4602', 'shape-11073', 'size-15027', 'size-15030', 'size-15033', 'height-3399', 'height-5884']
so I want to find the first matching value for each unique item.
for example, in the array, I have two strings with the shape prefix, six items with the size prefix, and two items with the height prefix.
so I want to output to be something like
const requiredVal = ["shape-10983", "size-2364", "height-3399"]
I want only the first value from any set of different values.
the simplest solution will be to iterate on the list and storing what you got in a dictionary
function removeSimilars(input) {
let values = {};
for (let value of input) {//iterate on the array
let key = value.splitOnLast('-')[0];//get the prefix
if (!(key in values))//if we haven't encounter the prefix yet
values[key] = value;//store that the first encounter with the prefix is with 'value'
}
return Object.values(values);//return all the values of the map 'values'
}
a shorter version will be this:
function removeSimilars(input) {
let values = {};
for (let value of input)
values[value.splitOnLast('-')[0]] ??= value;
return Object.values(values);
}
You could split the string and get the type and use it aks key for an object along with the original string as value. At result take only the values from the object.
const
data = ['shape-10983', 'size-2364', 'size-7800', 'size-4602', 'shape-11073', 'size-15027', 'size-15030', 'size-15033', 'height-3399', 'height-5884'],
result = Object.values(data.reduce((r, s) => {
const [type] = s.split('-', 1);
r[type] ??= s;
return r;
}, {}));
console.log(result);
If, as you mentioned in the comments, you have the list of prefixes already available, then all you have to do is iterate over those, to find each first element that starts with that prefix in your full list of possible values:
const prefixes = ['shape', 'size', 'height'];
const list = ['shape-10983', 'size-2364', 'size-7800', 'size-4602', 'shape-11073', 'size-15027', 'size-15030', 'size-15033', 'height-3399', 'height-5884']
function reduceTheOptions(list = [], prefixes = [], uniques = []) {
prefixes.forEach(prefix =>
uniques.push(
list.find(e => e.startsWith(prefix))
)
);
return uniques;
}
console.log(reduceTheOptions(list, prefixes));
Try this:
function getRandomSet(arr, ...prefix)
{
// the final values are load into the array result variable
result = [];
const randomItem = (array) => array[Math.floor(Math.random() * array.length)];
prefix.forEach((pre) => {
result.push(randomItem(arr.filter((par) => String(par).startsWith(pre))));
});
return result;
}
const mainArr = ['shape-10983', 'size-2364', 'size-7800', 'size-4602', 'shape-11073', 'size-15027', 'size-15030', 'size-15033', 'height-3399', 'height-5884'];
console.log("Random values: ", getRandomSet(mainArr, "shape", "size", "height"));
I modified the #ofek 's answer a bit. cuz for some reason the ??= is not working in react project.
function removeSimilars(input) {
let values = {};
for (let value of input)
if (!values[value.split("-")[0]]) {
values[value.split("-")[0]] = value;
}
return Object.values(values);
}
create a new array and loop over the first array and check the existing of element before in each iteration if not push it to the new array
I have an simple javascript array that may contain duplicates or maynot contain duplicates.
var names = [
['aaa','pin/test1.html'],
['bbb','pin/test2.html'],
['ttt','test.html'],
['ggg','test.html'],
['yyy','un/777.html'],
['ggg','test3.html'],
['nnn','test3.html'],
['eee','n/777.html'],
['sss','pin/test1.html'],
['xxx','pin/test2.html'],
['ppp','pin/test1.html'],
];
I need to find the duplicate filepath and put their name into new array. If there is no duplicate then assign its name in first and then assign '' after two values. I could point all the codes that I have tried but it doesnt work. I accept jquery solution also. The expected outcome is this.
var outcome = [
[['aaa','sss','ppp'], 'pin/test1.html'],
[['bbb','eee','xxx'], 'pin/test2.html'],
[['ttt','ggg',''], 'test.html'],
[['yyy','',''], 'un/777.html'],
[['ggg','nnn',''], 'test3.html'],
];
What I have tried is this
for (var i = 0; i < arr.length; i++) {
var uniqueNames = [];
$.each(arr[i], function (i, el) {
if ($.inArray(el, uniqueNames) === -1) uniqueNames.push(el);
});
console.log(uniqueNames);
}
You could take a hash table and an array of empty strings and find the next slot for the value.
The array is reduced by taking an object as accumulator and a destructure array as value (the first part of the array) and key (the second part, aka filepath).
Inside of Array#reduce, a property check with the key is made and if undefined, an array with the wanted structure (array with two items, the first is an array with three emty spaces and the key) is being assigned by using a logical nullish assignment ??=.
The next line assigns the value to the next free slot, an item with an empty string.
Finally the accumulator is returned.
To get only an array as result, a conversion of the values of the object takes place.
let names = [['aaa','pin/test1.html'], ['bbb','pin/test2.html'], ['ttt','test.html'], ['ggg','test.html'], ['yyy','un/777.html'], ['ggg','test3.html'], ['nnn','test3.html'], ['eee','n/777.html'], ['sss','pin/test1.html'], ['xxx','pin/test2.html'], ['ppp','pin/test1.html']],
grouped = Object.values(names.reduce((r, [v, k]) => {
r[k] ??= [Array(3).fill(''), k];
r[k][0][r[k][0].indexOf('')] = v;
return r;
}, {}));
console.log(grouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }
const aux = (names) => {
const hash = {};
let max = 0;
names.forEach(ele => {
if (!hash[ele[1]]) hash[ele[1]] = [];
hash[ele[1]].push(ele[0]);
max = Math.max(hash[ele[1]].length, max);
});
return Object.keys(hash).map(ele => [[...hash[ele], ...Array(max -hash[ele].length).fill("")], ele]);
}
var names = [
['aaa','pin/test1.html'],
['bbb','pin/test2.html'],
['ttt','test.html'],
['ggg','test.html'],
['yyy','un/777.html'],
['ggg','test3.html'],
['nnn','test3.html'],
['eee','n/777.html'],
['sss','pin/test1.html'],
['xxx','pin/test2.html'],
['ppp','pin/test1.html'],
];
console.log(aux(names))
This might help
You do not need jQuery for dealing with regular JS structure, you can achieve what you want with a simple code like this:
var names = [['aaa','pin/test1.html'],['bbb','pin/test2.html'],['ttt','test.html'],['ggg','test.html'],['yyy','un/777.html'],['ggg','test3.html'],['nnn','test3.html'],['eee','n/777.html'],['sss','pin/test1.html'],['xxx','pin/test2.html'],['ppp','pin/test1.html'],];
let lengthToFill = 0;
// collecting all the duplicates into a map
const pathMap = {};
names.forEach(name => {
// just in case if you're not familiar with array destructuring
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
const [pathName, path] = name;
// make sure we have an array to deal with
// just in case you're not familiar with Nullish coalescing operator (??)
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator
pathMap[path] = pathMap[path] ?? [];
pathMap[path].push(pathName);
// tracking the max number of elements we're adding into a single entry
lengthToFill = Math.max(lengthToFill, pathMap[path].length);
});
const result = Object.entries(pathMap).map(entry => {
// constructing new array entry based on the data we've collected so far
return [
entry[1].concat(Array(lengthToFill - entry[1].length).fill('')),
entry[0],
];
});
console.log(result);
This solution will work for any number of elements that you'd like to fill the array with ''. It makes sure that the length of final listing is the same for all entries.
data = [
{
_id: 1234,
name: 'ddd'
},
]
Let's say I have this Object array in JavaScript and I want to retrieve the object based on the _id.
I know I can just do filter like,
data.filter((item, key) => { return item._id === "1234" })
But I wonder if there is a faster way of doing this, maybe like retrieving as I am accessing a dictionary with a key O(1).
Any help?
You can use Array.prototype.find():
data.find(({ _id }) => _id === '1234')
// or without destructuring and implicit return
data.find(item => { return item._id === '1234' })
If you want O(1) lookups, I'd suggest converting to a Map
const mappedData = new Map(data.map(item => ([item._id, item])))
or via Array.prototype.reduce()
const mappedData = data.reduce((map, item) => map.set(item._id, item), new Map())
Note that this operation is at best O(n) but it only needs to happen once.
Now you can fetch items with O(1) time complexity
const item1234 = mappedData.get(1234)
No. If you want to access them in constant time complexity O(1) then use that property as the index or key.
Here, the indices are 0,1,2 etc. So you can get data[0], data[1] without searching.
But if you want to find something inside the value at that index, then you have to search and it is not a constant time complexity operation.
If you need to do this query more than one time, by converting the array to a lookup object will take o(n) at first time, but the later query will only take constant time complexity o(1)
const dataWithIdKey = data.reduce((acc, item) => {
acc[item._id] = item;
return acc;
}, {});
const item1234 = dataWithIdKey['1234']
on the other hand, if there is only one time query. the find function will stop to run when it found the item with desired key. or a for loop can also solve this problem as well. however, the worse case of both still take o(n)
data.find( ({_id}) => _id === '1234' );
for(let i = 0; i < data.length; i++){
if(data[i]._id === '1234'){
// do something here
break;
}
}