Combine array items from different properties in a JavaScript object - javascript

I'm trying create a function that will iterate an object of arrays, and return one array that concatenates each element from one array to each element in the other arrays:
Object like so:
kitchen = {
food: [".bacon",".bananas"],
drinks: [".soda",".beer"],
apps: ['.fritters','.wings']
}
Desired returned array:
[
".bacon.soda",".bacon.beer",
".bananas.soda",".bananas.beer",
".bacon.fritters",".bacon.wings",
".bananas.fritters",".bananas.wings",
".soda.fritters",".soda.wings",
".beer.fritters",".beer.wings"
]
I'm having difficulty wrapping my brain around how to accomplish this. One thought I had was to create another object and create a hash where each array item becomes a property and then looping through so I have something like:
newObj = {
".bacon": [".soda",".beer",".fritters",".wings"]
".bananas": [".soda",".beer"...etc]
etc...
}
then loop through each prop, concatenating the property on each array element into a new array? Not sure if that's overkill though?
Plain JS is fine, but if you have a coffeescript solution as well that would be great too.
Thanks

Here's a solution that makes use of CoffeeScript syntax (since you asked for a CoffeeScript answer and then deleted that request?):
kitchen =
food: [".bacon",".bananas"]
drinks: [".soda",".beer"]
apps: ['.fritters','.wings']
allGroups = Object.keys(kitchen).map (key) -> kitchen[key]
allValues = []
allGroups.forEach (group, i) ->
otherValues = Array.prototype.concat.apply [], allGroups.slice(i + 1)
group.forEach (v1) -> otherValues.forEach (v2) -> allValues.push(v1 + v2)
console.log(allValues)
Here is the plain JS version:
var kitchen = {
food: [".bacon", ".bananas"],
drinks: [".soda", ".beer"],
apps: ['.fritters', '.wings']
}
var allGroups = Object.keys(kitchen).map(function(key) {
return kitchen[key];
});
var allValues = []
allGroups.forEach(function(group, i) {
var otherValues = Array.prototype.concat.apply([], allGroups.slice(i + 1));
group.forEach(function(v1) {
otherValues.forEach(function(v2) {
allValues.push(v1 + v2);
});
});
});
console.log(allValues)

Try this:
var result = [];
var keys = Object.keys(kitchen);
for (var i = 0; i < keys.length; i++) {
kitchen[keys[i]].forEach(function(ingred1) {
for (var j = i+1; j < keys.length; j++) {
kitchen[keys[j]].forEach(function(ingred2) {
result.push(ingred1 + ingred2);
});
}
});
}
console.log(result);

Related

Algorithm to sort an array in JS

I have an array
var arr= [
["PROPRI","PORVEC"],
["AJATRN","PROPRI"],
["BASMON","CALVI"],
["GHICIA","FOLELI"],
["FOLELI","BASMON"],
["PORVEC","GHICIA"]
] ;
And I'm trying to sort the array by making the second element equal to the first element of the next, like below:
arr = [
["AJATRN","PROPRI"],
["PROPRI","PORVEC"],
["PORVEC","GHICIA"],
["GHICIA","FOLELI"],
["FOLELI","BASMON"],
["BASMON","CALVI"]
]
The context is : these are somes sites with coordinates, I want to identify the order passed,
For exemple, I have [A,B] [C,D] [B,C] then I know the path is A B C D
I finally have one solution
var rs =[];
rs[0]=arr[0];
var hasAdded=false;
for (var i = 1; i < arr.length; i++) {
hasAdded=false;
console.log("i",i);
for (var j = 0, len=rs.length; j < len; j++) {
console.log("j",j);
console.log("len",len);
if(arr[i][1]===rs[j][0]){
rs.splice(j,0,arr[i]);
hasAdded=true;
console.log("hasAdded",hasAdded);
}
if(arr[i][0]===rs[j][1]){
rs.splice(j+1,0,arr[i]);
hasAdded=true;
console.log("hasAdded",hasAdded);
}
}
if(hasAdded===false) {
arr.push(arr[i]);
console.log("ARR length",arr.length);
}
}
But it's not perfect, when it's a circle like [A,B] [B,C] [C,D] [D,A]
I can't get the except answer
So I really hope this is what you like to achieve so have a look at this simple js code:
var vector = [
["PROPRI,PORVEC"],
["AJATRN,PROPRI"],
["BASMON,CALVI"],
["GHICIA,FOLELI"],
["FOLELI,BASMON"],
["PORVEC,GHICIA"]
]
function sort(vector) {
var result = []
for (var i = 1; i < vector.length; i++) result.push(vector[i])
result.push(vector[0])
return (result)
}
var res = sort(vector)
console.log(res)
Note: Of course this result could be easily achieved using map but because of your question I'm quite sure this will just confuse you. So have a look at the code done with a for loop :)
You can create an object lookup based on the first value of your array. Using this lookup, you can get the first key and then start adding value to your result. Once you add a value in the array, remove the value corresponding to that key, if the key has no element in its array delete its key. Continue this process as long as you have keys in your object lookup.
var vector = [["PROPRI", "PORVEC"],["AJATRN", "PROPRI"],["BASMON", "CALVI"],["GHICIA", "FOLELI"],["FOLELI", "BASMON"],["PORVEC", "GHICIA"]],
lookup = vector.reduce((r,a) => {
r[a[0]] = r[a[0]] || [];
r[a[0]].push(a);
return r;
}, {});
var current = Object.keys(lookup).sort()[0];
var sorted = [];
while(Object.keys(lookup).length > 0) {
if(lookup[current] && lookup[current].length) {
var first = lookup[current].shift();
sorted.push(first);
current = first[1];
} else {
delete lookup[current];
current = Object.keys(lookup).sort()[0];
}
}
console.log(sorted);

Remove items from array using id with Javascript

I have a function like this: pickListSelect array is has all id (numbers) to delete objects in source array, and target array it is to push elements deleted from source array.
function copy(pickListSelect, source, target) {
var i, id;
for (i = 0; i < pickListSelect.length; i++) {
id = pickListSelect[i];
source.splice(id,1);
}
pickListSelect = [];
}
So what I need is delete specific object from source array. I tried with that code but for example if I need to delete object with id=5, it only deleted item 5 from the list.
The structure of source array is this:
[Object, Object, Object, Object, Object, Object, Object, Object, Object]
0:Object
plantId:1
plantName:"Plant 1"
...the rest of others are similar object
You need to find plant in your source by plantId first, and then delete it from original array and push to target. Open console and it should log deleted plants:
var plants = [
{
plantId: 1,
plantName: 'plant 1'
},
{
plantId: 2,
plantName: 'plant 2'
},
{
plantId: 3,
plantName: 'plant 3'
},
{
plantId: 4,
plantName: 'plant 4'
}
];
function copy(pickListSelect, source, target) {
var i, id, el;
for (i = 0; i < pickListSelect.length; i++) {
id = pickListSelect[i];
el = findPlant(source, id);
source.splice(source.indexOf(el), 1);
target.push(el);
}
}
function findPlant (arr, id) {
return arr.filter(function (plant) {
return plant.plantId == id
})[0]
}
var test = [];
copy([2,3], plants, test);
console.log(test);
When you use .splice you need to pass in the start index at which to splice and the amount of items to splice, try this:
source.splice(i,1); // i is your starting index here
array.splice(start, deleteCount[, item1[, item2[, ...]]])
MDN on .splice
Now in your actual code you need to check to see if the id matches and then splice using the above code:
function copy(pickListSelect, source, target) {
var i, id;
for (i = 0; i < pickListSelect.length; i++) {
if (pickListSelect[i].id === someId) {
source.splice(i,1);
}
}
pickListSelect = [];
}
You can take a look at this fiddler here.
I have used underscore.js to find the correct element from source and move it to the target array.
var copy = function(pickListSelect, source, target) {
for (i = 0; i < pickListSelect.length; i++) {
id = pickListSelect[i];
var deleteIndex = _.findIndex(source, {Id: id});
var deletedItem = source.splice(deleteIndex, 1);
target.push(deletedItem[0])
}
pickListSelect = [];
return target;
}
You're not looking up the index of the source array with a matching id. It might be better to do something like this.
var idsToRemove = {};
// build an object of ids to remove (effectively a hashset)
for (var i = 0; i < pickSelectList.length; i++) {
idsToRemove[pickSelectList[i]] = true;
}
// loop through the source array to find any objects with ids to remove
for (var j = 0; j < source.length; j++) {
if (source[j].plantId in idsToRemove) {
target.push(source.splice(j, 1));
}
}

push only unique elements in an array

I have array object(x) that stores json (key,value) objects. I need to make sure that x only takes json object with unique key. Below, example 'id' is the key, so i don't want to store other json objects with 'item1' key.
x = [{"id":"item1","val":"Items"},{"id":"item1","val":"Items"},{"id":"item1","val":"Items"}]
var clickId = // could be "item1", "item2"....
var found = $.inArray(clickId, x); //
if(found >=0)
{
x.splice(found,1);
}
else{
x.push(new Item(clickId, obj)); //push json object
}
would this accomplish what you're looking for? https://jsfiddle.net/gukv9arj/3/
x = [
{"id":"item1","val":"Items"},
{"id":"item1","val":"Items"},
{"id":"item2","val":"Items"}
];
var clickId = [];
var list = JSON.parse(x);
$.each(list, function(index, value){
if(clickId.indexOf(value.id) === -1){
clickId.push(value.id);
}
});
You can't use inArray() because you are searching for an object.
I'd recommend rewriting a custom find using Array.some() as follows.
var x = [{"id":"item1","val":"Items"},{"id":"item1","val":"Items"},{"id":"item1","val":"Items"}]
var clickId = "item1";
var found = x.some(function(value) {
return value.id === clickId;
});
alert(found);
Almost 6 years later i ended up in this question, but i needed to fill a bit more complex array, with objects. So i needed to add something like this.
var values = [
{value: "value1", selected: false},
{value: "value2", selected: false}
//there cannot be another object with value = "value1" within the collection.
]
So I was looking for the value data not to be repeated (in an object's array), rather than just the value in a string's array, as required in this question. This is not the first time i think in doing something like this in some JS code.
So i did the following:
let valueIndex = {};
let values = []
//I had the source data in some other and more complex array.
for (const index in assetsArray)
{
const element = assetsArray[index];
if (!valueIndex[element.value])
{
valueIndex[element.value] = true;
values.push({
value: element.value,
selected: false
});
}
}
I just use another object as an index, so the properties in an object will never be repated. This code is quite easy to read and surely is compatible with any browser. Maybe someone comes with something better. You are welcome to share!
Hopes this helps someone else.
JS objects are great tools to use for tracking unique items. If you start with an empty object, you can incrementally add keys/values. If the object already has a key for a given item, you can set it to some known value that is use used to indicate a non-unique item.
You could then loop over the object and push the unique items to an array.
var itemsObj = {};
var itemsList = [];
x = [{"id":"item1","val":"foo"},
{"id":"item2","val":"bar"},
{"id":"item1","val":"baz"},
{"id":"item1","val":"bez"}];
for (var i = 0; i < x.length; i++) {
var item = x[i];
if (itemsObj[item.id]) {
itemsObj[item.id] = "dupe";
}
else {
itemsObj[item.id] = item;
}
}
for (var myKey in itemsObj) {
if (itemsObj[myKey] !== "dupe") {
itemsList.push(itemsObj[myKey]);
}
}
console.log(itemsList);
See a working example here: https://jsbin.com/qucuso
If you want a list of items that contain only the first instance of an id, you can do this:
var itemsObj = {};
var itemsList = [];
x = [{"id":"item1","val":"foo"},
{"id":"item2","val":"bar"},
{"id":"item1","val":"baz"},
{"id":"item1","val":"bez"}];
for (var i = 0; i < x.length; i++) {
var item = x[i];
if (!itemsObj[item.id]) {
itemsObj[item.id] = item;
itemsList.push(item);
}
}
console.log(itemsList);
This is late but I did something like the following:
let MyArray = [];
MyArray._PushAndRejectDuplicate = function(el) {
if (this.indexOf(el) == -1) this.push(el)
else return;
}
MyArray._PushAndRejectDuplicate(1); // [1]
MyArray._PushAndRejectDuplicate(2); // [1,2]
MyArray._PushAndRejectDuplicate(1); // [1,2]
This is how I would do it in pure javascript.
var x = [{"id":"item1","val":"Items"},{"id":"item1","val":"Items"},{"id":"item1","val":"Items"}];
function unique(arr, comparator) {
var uniqueArr = [];
for (var i in arr) {
var found = false;
for (var j in uniqueArr) {
if (comparator instanceof Function) {
if (comparator.call(null, arr[i], uniqueArr[j])) {
found = true;
break;
}
} else {
if (arr[i] == uniqueArr[j]) {
found = true;
break;
}
}
}
if (!found) {
uniqueArr.push(arr[i]);
}
}
return uniqueArr;
};
u = unique(x, function(a,b){ return a.id == b.id; });
console.log(u);
y = [ 1,1,2,3,4,5,5,6,1];
console.log(unique(y));
Create a very readable solution with lodash.
x = _.unionBy(x, [new Item(clickId, obj)], 'id');
let x = [{id:item1,data:value},{id:item2,data:value},{id:item3,data:value}]
let newEle = {id:newItem,data:value}
let prev = x.filter(ele=>{if(ele.id!=new.id)return ele);
newArr = [...prev,newEle]

Get the index of the object inside an array, matching a condition

I have an array like this:
[{prop1:"abc",prop2:"qwe"},{prop1:"bnmb",prop2:"yutu"},{prop1:"zxvz",prop2:"qwrq"},...]
How can I get the index of the object that matches a condition, without iterating over the entire array?
For instance, given prop2=="yutu", I want to get index 1.
I saw .indexOf() but think it's used for simple arrays like ["a1","a2",...]. I also checked $.grep() but this returns objects, not the index.
As of 2016, you're supposed to use Array.findIndex (an ES2015/ES6 standard) for this:
a = [
{prop1:"abc",prop2:"qwe"},
{prop1:"bnmb",prop2:"yutu"},
{prop1:"zxvz",prop2:"qwrq"}];
index = a.findIndex(x => x.prop2 ==="yutu");
console.log(index);
It's supported in Google Chrome, Firefox and Edge. For Internet Explorer, there's a polyfill on the linked page.
Performance note
Function calls are expensive, therefore with really big arrays a simple loop will perform much better than findIndex:
let test = [];
for (let i = 0; i < 1e6; i++)
test.push({prop: i});
let search = test.length - 1;
let count = 100;
console.time('findIndex/predefined function');
let fn = obj => obj.prop === search;
for (let i = 0; i < count; i++)
test.findIndex(fn);
console.timeEnd('findIndex/predefined function');
console.time('findIndex/dynamic function');
for (let i = 0; i < count; i++)
test.findIndex(obj => obj.prop === search);
console.timeEnd('findIndex/dynamic function');
console.time('loop');
for (let i = 0; i < count; i++) {
for (let index = 0; index < test.length; index++) {
if (test[index].prop === search) {
break;
}
}
}
console.timeEnd('loop');
As with most optimizations, this should be applied with care and only when actually needed.
How can I get the index of the object tha match a condition (without iterate along the array)?
You cannot, something has to iterate through the array (at least once).
If the condition changes a lot, then you'll have to loop through and look at the objects therein to see if they match the condition. However, on a system with ES5 features (or if you install a shim), that iteration can be done fairly concisely:
var index;
yourArray.some(function(entry, i) {
if (entry.prop2 == "yutu") {
index = i;
return true;
}
});
That uses the new(ish) Array#some function, which loops through the entries in the array until the function you give it returns true. The function I've given it saves the index of the matching entry, then returns true to stop the iteration.
Or of course, just use a for loop. Your various iteration options are covered in this other answer.
But if you're always going to be using the same property for this lookup, and if the property values are unique, you can loop just once and create an object to map them:
var prop2map = {};
yourArray.forEach(function(entry) {
prop2map[entry.prop2] = entry;
});
(Or, again, you could use a for loop or any of your other options.)
Then if you need to find the entry with prop2 = "yutu", you can do this:
var entry = prop2map["yutu"];
I call this "cross-indexing" the array. Naturally, if you remove or add entries (or change their prop2 values), you need to update your mapping object as well.
What TJ Crowder said, everyway will have some kind of hidden iteration, with lodash this becomes:
var index = _.findIndex(array, {prop2: 'yutu'})
var CarId = 23;
//x.VehicleId property to match in the object array
var carIndex = CarsList.map(function (x) { return x.VehicleId; }).indexOf(CarId);
And for basic array numbers you can also do this:
var numberList = [100,200,300,400,500];
var index = numberList.indexOf(200); // 1
You will get -1 if it cannot find a value in the array.
var index;
yourArray.some(function (elem, i) {
return elem.prop2 === 'yutu' ? (index = i, true) : false;
});
Iterate over all elements of array.
It returns either the index and true or false if the condition does not match.
Important is the explicit return value of true (or a value which boolean result is true). The single assignment is not sufficient, because of a possible index with 0 (Boolean(0) === false), which would not result an error but disables the break of the iteration.
Edit
An even shorter version of the above:
yourArray.some(function (elem, i) {
return elem.prop2 === 'yutu' && ~(index = i);
});
Using Array.map() and Array.indexOf(string)
const arr = [{
prop1: "abc",
prop2: "qwe"
}, {
prop1: "bnmb",
prop2: "yutu"
}, {
prop1: "zxvz",
prop2: "qwrq"
}]
const index = arr.map(i => i.prop2).indexOf("yutu");
console.log(index);
The best & fastest way to do this is:
const products = [
{ prop1: 'telephone', prop2: 996 },
{ prop1: 'computadora', prop2: 1999 },
{ prop1: 'bicicleta', prop2: 995 },
];
const index = products.findIndex(el => el.prop2 > 1000);
console.log(index); // 1
I have seen many solutions in the above.
Here I am using map function to find the index of the search text in an array object.
I am going to explain my answer with using students data.
step 1: create array object for the students(optional you can create your own array object).
var students = [{name:"Rambabu",htno:"1245"},{name:"Divya",htno:"1246"},{name:"poojitha",htno:"1247"},{name:"magitha",htno:"1248"}];
step 2: Create variable to search text
var studentNameToSearch = "Divya";
step 3: Create variable to store matched index(here we use map function to iterate).
var matchedIndex = students.map(function (obj) { return obj.name; }).indexOf(studentNameToSearch);
var students = [{name:"Rambabu",htno:"1245"},{name:"Divya",htno:"1246"},{name:"poojitha",htno:"1247"},{name:"magitha",htno:"1248"}];
var studentNameToSearch = "Divya";
var matchedIndex = students.map(function (obj) { return obj.name; }).indexOf(studentNameToSearch);
console.log(matchedIndex);
alert("Your search name index in array is:"+matchedIndex)
You can use the Array.prototype.some() in the following way (as mentioned in the other answers):
https://jsfiddle.net/h1d69exj/2/
function findIndexInData(data, property, value) {
var result = -1;
data.some(function (item, i) {
if (item[property] === value) {
result = i;
return true;
}
});
return result;
}
var data = [{prop1:"abc",prop2:"qwe"},{prop1:"bnmb",prop2:"yutu"},{prop1:"zxvz",prop2:"qwrq"}]
alert(findIndexInData(data, 'prop2', "yutu")); // shows index of 1
function findIndexByKeyValue(_array, key, value) {
for (var i = 0; i < _array.length; i++) {
if (_array[i][key] == value) {
return i;
}
}
return -1;
}
var a = [
{prop1:"abc",prop2:"qwe"},
{prop1:"bnmb",prop2:"yutu"},
{prop1:"zxvz",prop2:"qwrq"}];
var index = findIndexByKeyValue(a, 'prop2', 'yutu');
console.log(index);
Try this code
var x = [{prop1:"abc",prop2:"qwe"},{prop1:"bnmb",prop2:"yutu"},{prop1:"zxvz",prop2:"qwrq"}]
let index = x.findIndex(x => x.prop1 === 'zxvz')
Another easy way is :
function getIndex(items) {
for (const [index, item] of items.entries()) {
if (item.prop2 === 'yutu') {
return index;
}
}
}
const myIndex = getIndex(myArray);
Georg have already mentioned ES6 have Array.findIndex for this.
And some other answers are workaround for ES5 using Array.some method.
One more elegant approach can be
var index;
for(index = yourArray.length; index-- > 0 && yourArray[index].prop2 !== "yutu";);
At the same time I will like to emphasize, Array.some may be implemented with binary or other efficient searching technique. So, it might perform better over for loop in some browser.
Why do you not want to iterate exactly ? The new Array.prototype.forEach are great for this purpose!
You can use a Binary Search Tree to find via a single method call if you want. This is a neat implementation of BTree and Red black Search tree in JS - https://github.com/vadimg/js_bintrees - but I'm not sure whether you can find the index at the same time.
One step using Array.reduce() - no jQuery
var items = [{id: 331}, {id: 220}, {id: 872}];
var searchIndexForId = 220;
var index = items.reduce(function(searchIndex, item, index){
if(item.id === searchIndexForId) {
console.log('found!');
searchIndex = index;
}
return searchIndex;
}, null);
will return null if index was not found.
var list = [
{prop1:"abc",prop2:"qwe"},
{prop1:"bnmb",prop2:"yutu"},
{prop1:"zxvz",prop2:"qwrq"}
];
var findProp = p => {
var index = -1;
$.each(list, (i, o) => {
if(o.prop2 == p) {
index = i;
return false; // break
}
});
return index; // -1 == not found, else == index
}

How to reduce a list of multiple items to a key value pair object in Javascript?

I have an array of states:
['CO','CA','CO','AL', ... ,'NV']
and I'd like to reduce to:
{ 'CO': 9, 'CA':17, 'AL':1, etc}
The value is the number of times each state occurs in the array.
what's the most efficient way to do this?
function compress2dict( raw_arr )
{
var ret={};
for(var i=0;i<raw_arr.length;i++)
{
var item=raw_arr[i];
ret[item]|=0;
ret[item]++;
}
return ret;
}
a = ['CO','BO','CO','CC','CC','CO','CC']
b = compress2dict(a)
b
{'BO':1, 'CC':3, 'CO':3}
You may be interested in array_count_values from PHPJS. Since the PHP array_count_values function does exactly what you want, it stands to reason that the JavaScript port of that function fits.
I expect you just iterate over the array, assign the member values to object property names and the number of occurences as the value:
function toObj(arr) {
var item, obj = {};
for (var i=0, iLen=arr.length; i<iLen; i++) {
item = arr[i];
obj[item]? ++obj[item] : (obj[item] = 1);
}
return obj;
}
Or if you like while loops (sometimes they're faster, sometimes not):
function toObj(arr) {
var item, obj = {}, i = arr.length;
while (i) {
item = arr[--i];
obj[item]? ++obj[item] : (obj[item] = 1);
}
return obj;
}

Categories