एक वैकल्पिक फैशन में दो सूचियों को संयोजित करने का पाइथोनिक तरीका?


88

मेरे पास दो सूचियां हैं, जिनमें से पहली में दूसरी की तुलना में एक अधिक आइटम शामिल करने की गारंटी है । मैं एक नई सूची बनाने के लिए सबसे पायथोनिक तरीका जानना चाहूंगा, जिसका समान-सूचकांक मूल्य पहली सूची से आता है और जिसका विषम-सूचकांक मान दूसरी सूची से आता है।

# example inputs
list1 = ['f', 'o', 'o']
list2 = ['hello', 'world']

# desired output
['f', 'hello', 'o', 'world', 'o']

यह काम करता है, लेकिन सुंदर नहीं है:

list3 = []
while True:
    try:
        list3.append(list1.pop(0))
        list3.append(list2.pop(0))
    except IndexError:
        break

इसे और कैसे हासिल किया जा सकता है? सबसे पायथोनिक दृष्टिकोण क्या है?



डुप्लिकेट नहीं! ऊपर से जुड़े लेख में स्वीकृत उत्तर टुपल्स की सूची बनाता है, एक एकल नहीं, विलय की गई सूची।
— पॉल ससिक

@Paul: हां, स्वीकृत उत्तर पूर्ण समाधान नहीं देता है। टिप्पणियों और अन्य उत्तरों को पढ़ें। प्रश्न मूल रूप से एक ही है और अन्य समाधान यहां लागू किए जा सकते हैं।
— फेलिक्स क्लिंग सेप

3
@ फेलिक्स: मैं सम्मान से असहमत हूं। यह सच है, सवाल एक ही पड़ोस में हैं लेकिन वास्तव में डुप्लिकेट नहीं हैं। जैसा कि अस्पष्ट प्रमाण यहां संभावित उत्तरों पर एक नज़र रखता है और दूसरे प्रश्न के साथ तुलना करता है।
— पॉल ससिक

इन्हें देखें: stackoverflow.com/questions/7529376/…
— wordsforthewise

जवाबों:


110

यहां एक तरीका है इसे स्लाइस करके:

>>> list1 = ['f', 'o', 'o']
>>> list2 = ['hello', 'world']
>>> result = [None]*(len(list1)+len(list2))
>>> result[::2] = list1
>>> result[1::2] = list2
>>> result
['f', 'hello', 'o', 'world', 'o']

3
धन्यवाद, डंकन। मुझे एहसास नहीं हुआ कि टुकड़ा करते समय एक कदम निर्दिष्ट करना संभव है। मुझे इस दृष्टिकोण के बारे में क्या पसंद है कि यह स्वाभाविक रूप से कैसे पढ़ता है। 1. सही लंबाई की एक सूची बनाओ। 2. सूची 1 की सामग्री के साथ भी अनुक्रमित आबादी। 3. सूची 2 की सामग्री के साथ विषम अनुक्रमित करें। तथ्य यह है कि सूचियां अलग-अलग लंबाई की हैं इस मामले में कोई समस्या नहीं है!
— दाविदचमर्स

2
मुझे लगता है कि यह केवल तभी काम करता है जब len (list1) - len (list2) 0 या 1. होता है
— xan

1
यदि सूचियाँ उचित लंबाई की हैं तो यह काम करती है, यदि नहीं तो मूल प्रश्न यह नहीं बताता है कि उत्तर क्या है। यह सबसे उचित स्थितियों को संभालने के लिए आसानी से संशोधित किया जा सकता है: उदाहरण के लिए, यदि आप चाहते थे कि अतिरिक्त तत्वों को अनदेखा किया जाए तो शुरू होने से पहले लंबी सूची को काट दें; यदि आप चाहते हैं कि अतिरिक्त तत्व किसी के साथ भी हस्तक्षेप न करें, तो बस यह सुनिश्चित करें कि परिणाम कुछ और के साथ आरंभ हुआ है; यदि आप अंत में अतिरिक्त तत्वों को जोड़ना चाहते हैं, तो उन्हें अनदेखा करने के लिए करें और फिर उन्हें संलग्न करें।
— डंकन

1
मैं भी अस्पष्ट था। मैं जिस बिंदु को बनाने की कोशिश कर रहा था, वह यह है कि डंकन का समाधान, सूचीबद्ध लोगों में से कई के विपरीत, इस तथ्य से जटिल नहीं है कि सूचियां असमान लंबाई की हैं। निश्चित रूप से, यह केवल सीमित परिस्थितियों में ही लागू होता है, लेकिन मैं वास्तव में सुरुचिपूर्ण समाधान पसंद करूंगा जो इस उदाहरण में कम सुरुचिपूर्ण समाधान में काम करता है जो कि किसी भी दो सूचियों के लिए काम करता है।
— davidchambers

1
आप (len (list1) + len (list2)) के बजाय (2 * len (list1) -1) का उपयोग कर सकते हैं, मैं भी [:: 2] के बजाय [0 :: 2] को पसंद करता हूं।
— लॉर्ड ब्रिटिश

50

itertoolsप्रलेखन में इसके लिए एक नुस्खा है :

from itertools import cycle, islice

def roundrobin(*iterables):
    "roundrobin('ABC', 'D', 'EF') --> A D E B F C"
    # Recipe credited to George Sakkis
    pending = len(iterables)
    nexts = cycle(iter(it).next for it in iterables)
    while pending:
        try:
            for next in nexts:
                yield next()
        except StopIteration:
            pending -= 1
            nexts = cycle(islice(nexts, pending))

संपादित करें:

अजगर के संस्करण के लिए 3 से अधिक:

from itertools import cycle, islice

def roundrobin(*iterables):
    "roundrobin('ABC', 'D', 'EF') --> A D E B F C"
    # Recipe credited to George Sakkis
    pending = len(iterables)
    nexts = cycle(iter(it).__next__ for it in iterables)
    while pending:
        try:
            for next in nexts:
                yield next()
        except StopIteration:
            pending -= 1
            nexts = cycle(islice(nexts, pending))

मैं इस तरह से इसे और अधिक जटिल होने की आवश्यकता है। उपयोग करने के लिए नीचे एक बेहतर विकल्प है zip_longest।
— डबस्लो

@Dubslow इस विशेष मामले के लिए, हाँ, यह संभवतः ओवरकिल है (जैसा कि मैंने एक टिप्पणी में कहीं और उल्लेख किया है), जब तक कि आप पहले से ही इसका उपयोग करने के लिए नहीं होते हैं। हालांकि अन्य स्थितियों में इसके कुछ फायदे हो सकते हैं। यह नुस्खा निश्चित रूप से इस समस्या के लिए नहीं बनाया गया था, यह सिर्फ इसे हल करने के लिए होता है।
— डेविड जेड

1
फी, आपको itertools दस्तावेज़ में नुस्खा का उपयोग करना चाहिए क्योंकि .next()अब काम नहीं करता है।
— जॉन डब्ल्यू।

1
@johnw। एक का उपयोग करना है __next__। यह दस्तावेज में नहीं लिखा है, इसलिए मैंने उत्तर को संपादित करने का प्रस्ताव दिया।
— समुद्री गैलेन्टिन

@ मेरी तरह मैं बल्कि आप मौजूदा कोड नमूने को बदल दिया था, लेकिन मैं खुद को ठीक कर सकते हैं। योगदान के लिए धन्यवाद!
— डेविड जेड

31

यह वही करना चाहिए जो आप चाहते हैं:

>>> iters = [iter(list1), iter(list2)]
>>> print list(it.next() for it in itertools.cycle(iters))
['f', 'hello', 'o', 'world', 'o']

मुझे वास्तव में आपका प्रारंभिक उत्तर पसंद आया। यद्यपि यह प्रश्न को पूरी तरह से संबोधित नहीं करता था, यह एक ही लंबाई की दो सूचियों को विलय करने का एक सुंदर तरीका था। मैं सुझाव देता हूं कि इसे बनाए रखने के साथ-साथ, अपने वर्तमान उत्तर में, यह स्पष्ट है।
— पॉल शशिक

1
यदि list1 इसके बजाय ['f', 'o', 'o', 'd'] थे, तो इसका अंतिम आइटम ('d') परिणामी सूची में नहीं दिखाई देगा (जो कि प्रश्न की बारीकियों को देखते हुए पूरी तरह से ठीक है)। यह एक सुंदर समाधान है!
— davidchambers 18


4
बताई गई समस्या को हल करने के लिए +1, और इसे बस भी करने के लिए :-) मुझे लगा कि ऐसा कुछ संभव होगा। ईमानदारी से मुझे लगता है कि roundrobinइस स्थिति के लिए फ़ंक्शन ओवरकिल की तरह है।
— डेविड जेड

1
किसी भी आकार की सूचियों के साथ काम करने के लिए आप केवल यह बता सकते हैं कि पुनरावृत्तियों में परिणाम क्या है:list(itertools.chain(map(next, itertools.cycle(iters)), *iters))
— पांडा -34

29
import itertools
print [x for x in itertools.chain.from_iterable(itertools.izip_longest(list1,list2)) if x]

मुझे लगता है कि यह इसे करने का सबसे अधिक पायथोनिक तरीका है।


3
यह स्वीकृत उत्तर क्यों नहीं है? यह सबसे छोटा और सबसे अजगर है और विभिन्न सूची लंबाई के साथ काम करता है!
— जायरो वादिलो

5
विधि का नाम zip_longest izip_longest नहीं है
— Jairo Vadillo

1
इसके साथ समस्या यह है कि zip_longest से डिफ़ॉल्ट फ़िलिन मान Noneउस सूची में हो सकता है जो * को अधिलेखित कर सकता है। मैं इसे ठीक करने के लिए एक साझा संस्करण में संपादित
— करूंगा

नोट: यह समस्या पैदा करेगा यदि सूचियों में मूल्य के साथ तत्व होते हैं False, या यहां तक ​​कि ऐसी चीजों का मूल्यांकन किया जाएगा जो केवल -कंपनी Falseद्वारा मूल्यांकन की जाती ifहैं, उदाहरण के लिए 0या एक खाली सूची। यह (आंशिक रूप से) निम्नलिखित से बचा जा सकता है [x for x in itertools.chain.from_iterable(itertools.zip_longest(list1, list2)) if x is not None]:। बेशक, यह तब भी काम नहीं करेगा यदि सूचियों में ऐसे Noneतत्व शामिल हैं जिन्हें संरक्षित करने की आवश्यकता है। इस मामले में, आपको fillvalueतर्क बदलने की जरूरत है zip_longest, क्योंकि डबस्लो ने पहले ही सुझाव दिया था।
— der_herr_g

Noneसमस्या लगती है, कम से कम पाइथन 3.7.6 के बाद से (मुझे पुराने संस्करणों के लिए पता नहीं है)। यदि alt_chainके रूप में परिभाषित किया गया है def alt_chain(*iters, fillvalue=None): return chain.from_iterable(zip_longest(*iters, fillvalue=fillvalue)), तो list(alt_chain([0, False, 1, set(), 3, 4], [0, None, 1, {}], fillvalue=99))सही ढंग से रिटर्न [0, 0, False, None, 1, 1, set(), {}, 3, 99, 4, 99]।
— पॉइम

17

Itertools के बिना और l1 मानकर l2 से 1 आइटम लंबा है:

>>> sum(zip(l1, l2+[0]), ())[:-1]
('f', 'hello', 'o', 'world', 'o')

Itertools का उपयोग करना और यह मानना ​​कि सूचियों में कोई नहीं है:

>>> filter(None, sum(itertools.izip_longest(l1, l2), ()))
('f', 'hello', 'o', 'world', 'o')

यह मेरा पसंदीदा उत्तर है। यह बहुत संक्षिप्त है।
— mbomb007

@ anishtain4 ज़िप, सूची से tuples के रूप में तत्वों की जोड़ी लेता है [(l1[0], l2[0]), (l1[1], l2[1]), ...]। sumएक साथ टुपल्स (l1[0], l2[0]) + (l1[1], l2[1]) + ...को संघटित करता है : जिसके परिणामस्वरूप इंटरलेव्ड सूचियां होती हैं। एक-लाइन का बाकी काम करने के लिए ज़िप के लिए अतिरिक्त तत्व के साथ एल 1 का सिर्फ पैडिंग है और उस पैडिंग से छुटकारा पाने के लिए -1 तक स्लाइस।
— शाम

izip_longest (python 3 के बाद से zip_longest) को [+ 0] पैडिंग की आवश्यकता नहीं है, यह अंतर्निहित रूप से कोई नहीं भरता है जब सूचियों की लंबाई मेल नहीं खाती है, जबकि filter(None, ...( boolया इसके बजाय का उपयोग कर सकते हैं None.__ne__) 0, कोई नहीं और खाली स्ट्रिंग्स सहित, गलत मान निकालता है, इसलिए दूसरी अभिव्यक्ति पहले वाले के बराबर कड़ाई से नहीं है।
— शाम

सवाल यह है कि आपने ऐसा कैसे किया sum? वहां दूसरे तर्क की क्या भूमिका है? दस्तावेजों में, दूसरा तर्क है start।
— ऐशट्रेन

प्रारंभ का डिफ़ॉल्ट मान 0 है, और आप 0+ (कुछ, tuple) नहीं कर सकते, इस प्रकार प्रारंभ को tuple में बदल दिया जाता है।
— Zart

13

मुझे पता है कि प्रश्न दो सूचियों के बारे में पूछते हैं जिनमें एक के बाद एक आइटम एक से अधिक हैं, लेकिन मुझे लगा कि मैं इसे उन लोगों के लिए रखूंगा जो इस प्रश्न को ढूंढ सकते हैं।

यहां डंकन के समाधान को विभिन्न आकारों की दो सूचियों के साथ काम करने के लिए अनुकूलित किया गया है।

list1 = ['f', 'o', 'o', 'b', 'a', 'r']
list2 = ['hello', 'world']
num = min(len(list1), len(list2))
result = [None]*(num*2)
result[::2] = list1[:num]
result[1::2] = list2[:num]
result.extend(list1[num:])
result.extend(list2[num:])
result

यह आउटपुट:

['f', 'hello', 'o', 'world', 'o', 'b', 'a', 'r'] 

6

यदि दोनों सूचियों की लंबाई समान है, तो आप यह कर सकते हैं:

[x for y in zip(list1, list2) for x in y]

चूंकि पहली सूची में एक और तत्व है, आप इसे पोस्ट हॉक जोड़ सकते हैं:

[x for y in zip(list1, list2) for x in y] + [list1[-1]]

2
^ यह जवाब होना चाहिए, अजगर पिछले 10 वर्षों में अधिक अजगर बन गया है
— तियान

5

यहाँ एक लाइनर है जो इसे करता है:

list3 = [ item for pair in zip(list1, list2 + [0]) for item in pair][:-1]


2
यह सही ढंग से काम करता है लेकिन मुझे अयोग्य के रूप में मारता है क्योंकि यह इतना सरल कुछ हासिल करने के लिए बहुत कुछ कर रहा है। मैं यह नहीं कह रहा हूं कि यह दृष्टिकोण अक्षम है, बस यह पढ़ना आसान नहीं है।
— davidchambers

2
def combine(list1, list2):
    lst = []
    len1 = len(list1)
    len2 = len(list2)

    for index in range( max(len1, len2) ):
        if index+1 <= len1:
            lst += [list1[index]]

        if index+1 <= len2:
            lst += [list2[index]]

    return lst

परिवर्तनशील डिफ़ॉल्ट तर्कों का उपयोग करते समय बहुत सावधान रहें। यह केवल सही उत्तर को पहली बार वापस बुलाया जाएगा, क्योंकि इसके बाद हर कॉल के लिए पुन: उपयोग किया जाएगा। यह बेहतर होगा lst के रूप में लिखा है = कोई नहीं ... यदि lst कोई नहीं है: lst = [], हालांकि मुझे यहां सूचीबद्ध अन्य लोगों के लिए इस दृष्टिकोण को चुनने के लिए एक सम्मोहक कारण नहीं दिखता है।
— davidchambers

lst को फ़ंक्शन के भीतर परिभाषित किया गया है, इसलिए यह एक स्थानीय चर है। संभावित समस्या यह है कि सूची 1 और सूची 2 का उपयोग हर बार जब आप फ़ंक्शन का उपयोग करते हैं, भले ही आप अलग-अलग सूचियों के साथ फ़ंक्शन को कॉल करते हैं। देखें docs.python.org/tutorial/...
— blokeley

1
@blokeley: गलत, अगर इसका संयोजन (सूची 1 = [...], सूची 2 = [...]) के साथ पुन: उपयोग किया जाएगा
— बजे

जब इस समाधान को पहली बार इसकी पहली पंक्ति पढ़ी गई थी def combine(list1, list2, lst=[]):, तो मेरी टिप्पणी। जब तक मैंने वह टिप्पणी प्रस्तुत की, तब तक किलोस ने आवश्यक बदलाव कर दिया था।
— davidchambers

2

यह एक कार्लोस वालियंटे के योगदान पर आधारित है, जिसमें कई मदों के वैकल्पिक समूहों के विकल्प के साथ यह सुनिश्चित किया गया है कि सभी आइटम आउटपुट में मौजूद हैं:

A=["a","b","c","d"]
B=[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]

def cyclemix(xs, ys, n=1):
    for p in range(0,int((len(ys)+len(xs))/n)):
        for g in range(0,min(len(ys),n)):
            yield ys[0]
            ys.append(ys.pop(0))
        for g in range(0,min(len(xs),n)):
            yield xs[0]
            xs.append(xs.pop(0))

print [x for x in cyclemix(A, B, 3)]

यह ए और बी को 3 मानों के समूहों द्वारा सूचीबद्ध करेगा:

['a', 'b', 'c', 1, 2, 3, 'd', 'a', 'b', 4, 5, 6, 'c', 'd', 'a', 7, 8, 9, 'b', 'c', 'd', 10, 11, 12, 'a', 'b', 'c', 13, 14, 15]


1

यहाँ सूची बोधक, w / o अन्य पुस्तकालयों का उपयोग करते हुए एक लाइनर है:

list3 = [sub[i] for i in range(len(list2)) for sub in [list1, list2]] + [list1[-1]]

यहाँ एक और दृष्टिकोण है, यदि आप साइड इफेक्ट द्वारा अपनी प्रारंभिक सूची १ में फेरबदल की अनुमति देते हैं:

[list1.insert((i+1)*2-1, list2[i]) for i in range(len(list2))]

1

थोड़ा और देर हो सकती है अभी तक एक और अजगर एक लाइनर खरीदते हैं। यह तब काम करता है जब दो सूचियों में समान या असमान आकार होता है। एक बात के लायक कुछ भी नहीं है यह एक और बी को संशोधित करेगा। यदि यह एक समस्या है, तो आपको अन्य समाधानों का उपयोग करने की आवश्यकता है।

a = ['f', 'o', 'o']
b = ['hello', 'world']
sum([[a.pop(0), b.pop(0)] for i in range(min(len(a), len(b)))],[])+a+b
['f', 'hello', 'o', 'world', 'o']


0

सबसे कम पर रुकती है:

def interlace(*iters, next = next) -> collections.Iterable:
    """
    interlace(i1, i2, ..., in) -> (
        i1-0, i2-0, ..., in-0,
        i1-1, i2-1, ..., in-1,
        .
        .
        .
        i1-n, i2-n, ..., in-n,
    )
    """
    return map(next, cycle([iter(x) for x in iters]))

निश्चित रूप से, अगले / __ next__ विधि को हल करना तेज हो सकता है।


0

यह बुरा है लेकिन सूचियों के आकार से कोई फर्क नहीं पड़ता:

list3 = [element for element in list(itertools.chain.from_iterable([val for val in itertools.izip_longest(list1, list2)])) if element != None]


0

सुन्न के बारे में कैसे? यह तार के साथ भी काम करता है:

import numpy as np

np.array([[a,b] for a,b in zip([1,2,3],[2,3,4,5,6])]).ravel()

परिणाम:

array([1, 2, 2, 3, 3, 4])

0

एक कार्यात्मक और अपरिवर्तनीय तरीके से विकल्प (पायथन 3):

from itertools import zip_longest
from functools import reduce

reduce(lambda lst, zipped: [*lst, *zipped] if zipped[1] != None else [*lst, zipped[0]], zip_longest(list1, list2),[])

-1

मैं सरल करूँगा:

chain.from_iterable( izip( list1, list2 ) )

यह किसी भी अतिरिक्त भंडारण जरूरतों को बनाने के बिना एक पुनरावृत्ति के साथ आएगा।


1
यह वास्तव में सरल है, लेकिन यह केवल एक ही लंबाई की सूचियों के साथ काम करता है!
— जोहान रिट्जेल

आप इसे chain.from_iterable(izip(list1, list2), list1[len(list2):])यहाँ पूछे गए विशेष समस्या के लिए ठीक कर सकते हैं ... सूची 1 को लंबा माना जाता है।
— जोहान रिट्जेल

हाँ, लेकिन मैं या तो एक समाधान के साथ आता हूं जो मनमाने ढंग से लंबाई के कंटेनरों के लिए काम करता है या ऊपर प्रस्तावित समाधानों के लिए उपज।
— गेहूं

-2

मैं सूची बोध से नीचे होने के लिए बहुत पुराना हूं, इसलिए:

import operator
list3 = reduce(operator.add, zip(list1, list2))
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.